I am processing a bunch of records and producing an array of hashes for a third party library. For the life of me I can't figure out why this doesn't work.
export default Route.extend({
model: function(params) {
let qp = {viewName: 'byDay'};
return this.store.query('job-receipt', qp).then(
(receipts)=>
all(
receipts.map(
(receipt)=>
hash({
stockCode: receipt.get('job')
.then(job => job.get('stockCode'))
.then(stockCode => stockCode.get('stockCode')),
productClass: receipt.get('job')
.then(job => job.get('stockCode'))
.then(stockCode => stockCode.get('productClass'))
.then(productClass => productClass.get('descr')),
qtyRecvd: receipt.get('qtyRecvd')
})
)
)
);
If I keep reentering the route, eventually the promises resolve. If I check, the productClass promise just straight up gets called with a null value. Why isn't it waiting for the stockCode.get('productClass') to resolve? I know there are real values in there because it does eventually resolve.
I'm missing something super basic. I've tried Ember.get(thing, keypath) etc. Don't these all return promises? Isn't the RSVP.hash supposed to wait for all the promises to resolve before proceeding? Like I said, I know the data is good because eventually it does resolve (as opposed to me just not handling a rejection).
EDIT:
I changed the productClass promise to this:
productClass: receipt.get('job')
.then(job => job.get('stockCode'))
.then(stockCode => stockCode.get('productClass'))
.then(productClass => {if (!productClass) {return 'foo';} return productClass.get('descr');})
Now the report renders correctly every time albeit with nonsense. If I navigate to another route, and then back to this route, it renders perfectly. So, that makes it hard for me to believe I have some kind of data error. And even some of the stock codes return the right product class - not 'foo' - on the first run through. I'm not even sure how to debug this further.
edit
Just saw this. May be a bug after all.
[3.2.0+] Snapshot’s related data has become null #5565
It turns out it is a bug. The bug for belongs-to.js causes the model to not wait for internalModel to complete loading before resolving the promise. The fix linked below resolves the issue
[BUGFIX] use internalModel promise if already loading #5562
I think the main issue is that "reciepts.job" is likely a DS.belongsTo relationship right? If you switch it to load job: DS.belongsTo('job', {async: false}), that will force ember-data to load that property synchronously (and will save a lot of headaches). But that requires the data to be available either in the json response.
If that doesn't work, you should investigate ember concurrency. Use that to clean up your code to look a little more straightforward. You'll have to fill in some blanks or change things where I misunderstood your use case, but this is a likely a good starting point.
The idea is to continue to break all the asynchronous calls into seperate tasks. Each ember-concurrency task object returns a promise, so you can keep bundling them up until you get to model which you can return just like any other promise.
//model.js
import {task} from 'ember-concurrency';
.....
model() {
return this.get('loadData').perform();
},
loadData: task(function*(){
let reciepts = yield this.store.query('job-receipt', qp);
let promises = reciepts.map(r => {
return this.get('loadNestedData').perform(r);
})
return all(promises)
}),
loadNestedData: task(function*(reciept) {
let job = yield receipt.get('job');
return hash({
stockCode: job.get('sockcode')
});
})
Related
I'm trying to create an ember data record with a hasMany relationship from a POJO, and it's been remarkably difficult. This is using ember-concurrency (hence the task and yield syntax.
saveEntry: task(function* (obj){
let entry = yield this.model.get('entries').createRecord({
groupId: obj.objectID,
name: obj.name,
...
}).save();
obj.owner_ids.forEach((user_id) => {
this.store.findRecord('user', user_id).then((user) => {
entry.get('owners').pushObject(user);
entry.save();
});
})
}).drop();
So the tough bits are the 'obj.owner_ids', which is an array of user_ids. I add each to the array, but then need to save after every pushObject or it doesn't work. And even when it does work, it is throwing a bunch of network errors (probably due to race conditions on the save). There must be a better way, but I haven't found it.
I'm not sure what this.model represents in your code, but you could try fetching all of the users you need and then attaching them at the time you create and save like:
import { all } from 'rsvp';
saveEntry: task(function* (obj){
let users = obj.owner_ids.map(userId => this.store.findRecord('user', user_id));
let promises = this.model.get('entries').createRecord({
groupId: obj.objectID,
name: obj.name,
owners: users,
...
}).save()
// all() waits for an an array of promises to return
yield all(promises);
}).drop();
I am trying to use AWS X-Ray in my SailsJS application. I noticed missing subsegments - I added custom traces via AWSXRay.captureAsyncFunc but noticed they are missing. After some closer inspection, I think they actually ended up in a different trace. Lets say I call login API then another API later. I notice my login API trace is weird
Notice theres calls quite abit after the request should have ended.
Those requests should actually be in another segment:
I'd think they should appear after find device subsegment. Why are segments scrambled like that?
My setup: in http.js,
const AWSXRay = require('aws-xray-sdk');
const xrayEnabled = process.env.AWS_XRAY === 'yes'
module.exports.http = {
middleware: {
order: [
'startRequestTimer',
'cookieParser',
'session',
'myRequestLogger',
'bodyParser',
'handleBodyParserError',
'compress',
'methodOverride',
'poweredBy',
'awsXrayStart',
'router',
'awsXrayEnd',
'www',
'favicon',
'404',
'500',
],
awsXrayStart: xrayEnabled ? AWSXRay.express.openSegment(`cast-${process.env.NODE_ENV || 'noenv'}`) : (req, res, next) => next(),
awsXrayEnd: xrayEnabled ? AWSXRay.express.closeSegment() : (req, res, next) => next(),
Then I wrapped my promises like:
instrumentPromise(promise, name, metadata = {}) {
if (this.isXrayEnabled()) {
return new Promise((resolve, reject) => {
AWSXRay.captureAsyncFunc(name, (subsegment) => {
if (!subsegment) console.warn(`[XRAY] Failed to instrument ${name}`)
Object.keys(metadata).forEach(k => {
if (subsegment) subsegment.addMetadata(k, metadata[k])
})
console.time(`[XRAY TIME] ${name}`)
promise
.then((data) => {
if (subsegment) subsegment.close()
console.timeEnd(`[XRAY TIME] ${name}`)
resolve(data)
})
.catch(err => {
if (subsegment) subsegment.close()
console.timeEnd(`[XRAY TIME] ${name}`)
reject(err)
})
})
})
}
return promise
}
Is there any information I am missing here? What am I doing wrong?
I tried manual mode and its alot more reliable but I have to manually pass segment around. Whats wrong with automatic mode? I am kind of guessing it does not work well with async nature nodejs? Like the SDK is not able to differenciate between the various async requests? And may close or track segments in the wrong places? That said ... its supposed to work with express, why isit not working as expected ...
Another thing is how will a shared mysql connection pool be tracked correctly by X-Ray? Different segments will be using the same mysql pool. I assume this will not work work well at all?
The issue you encounter seems to be related to how CLS handle context binding with Promise. There is a opt-in promise patch introduced in this PR https://github.com/aws/aws-xray-sdk-node/pull/11. It has full discussion around the repros and fixes. That should resolve the issue with subsegments being attached to the wrong trace.
The SDK does support capturing pool.query. You can see examples here https://www.npmjs.com/package/aws-xray-sdk-mysql.
Well I am new to ember js and i am stuck at a place. I wanted to know how can one use ember-cli-sofa to retrieve all the documents at once? Well I am using couchdb as backend and ember-cli-sofa helps to interact with database.Please give any tips how to approach this problem.
If you're fine without using models,
let db = store.get('db.main.documents');
db.all({ include_docs: true }); // => Promise
db.view('ddocName', 'viewName', { include_docs: true }); // => Promise
db.all calls _all_docs while db.view calls /_design/<ddoc>/_view/<view>
Here's a simple component:
App.FooBarComponent = Ember.Component.extend({
tagName: "button",
status: "Ready",
revertStatusPeriodMs: 2000,
click: function() {
this.set('status', 'Pending');
// A fake ajax
this.run()
.then( function() {
this.updateStatus('Finished');
}.bind(this))
.catch( function() {
this.updateStatus('Error');
}.bind(this));
},
run: function() {
return new Ember.RSVP.Promise( function(resolve) {
Ember.run.later( function() {
resolve();
}, 500);
});
},
updateStatus: function(statusText) {
this.set('status', statusText);
var periodMs = this.get('revertStatusPeriodMs') || 1000;
Ember.run.later( function() {
this.set('status', 'Ready');
}.bind(this), periodMs);
}
});
It does a simple thing: when clicked, it displays some text. Later replaces the text with another one. Even later, it reverts the text to the initial one.
The code works fine. My problem is that i'm unable to write a test for it.
test('clicking the button should set the label', function(assert) {
expect(4);
visit('/');
assert.equal( find('button').text().trim(), 'Ready', 'Should initially be "Ready"');
andThen(function() {
click('button');
assert.equal( find('button').text().trim(), 'Pending', 'Should "Pending" right after click');
});
// This one fires too late!
andThen(function() {
assert.equal( find('button').text().trim(), 'Finished', 'Should become "Finished" after promise fulfills');
});
andThen(function() {
assert.equal( find('button').text().trim(), 'Ready', 'Should eventually return to the "Ready" state');
});
});
I have two problems:
I'm unable to test the Finished state. It seems that andThen waits for all promises and run.laters to finish, while i want to test an intermediate state. How do i run an assertion between two sequential promises/run.laters?
Times can be long, and tests can take forever to complete. I generally don't mind refactoring the code for better testability, but i refuse to adjust times in the app based on the environment (e. g. 2000 for dev/prod, 0 for test). Instead, i would like to use a timer mock or some other solution.
I've tried Sinon and failed: when i mock the timer, andThen never returns. Neither of these solutions helped me.
JSBin: http://emberjs.jsbin.com/fohava/2/edit?html,js,output (Sinon is included)
None of your test code seems to use asynch – which would seem to be crucial since without anything to distinguish them, the last 2 tests will execute in the same tick (tests 3 & 4 can't both be true at the same time). I'm not familiar with QUnit, but I did find it's async method, which would seem to be pertinent. However when I tried calling assert.async, it blew up. I tried updating QUnit 1.17, still no dice.
So I don't have a solution for you. The problem, though, is that Ember will only execute both andThen tests once all the asynchronous execution has finished – this means that test 3 will never be true.
I did some code where I basically got it working but it seems really finicky and not something you would want to be do be doing..
I did this a few days ago but never replied because I wasn't happy with it - it doesn't give you enough control..
The reason I'm replying now however is because I've found a PR that may be of use to you: PR 10463
I didn't look into too much detail but apparently it "allows custom async test helpers to pause execution by returning a promise"
I'm not sure what version of Ember you're using - but if you can hold out for this PR it may help you..
On the Ember MVC TodoApp there is an option "Clear all Completed".
I've been trying to do a simple "Clear All".
I've tried multiple things, none of them work as I expected (clearing the data, the local storage and refreshing the UI).
The ones that comes with the sample is this code below:
clearCompleted: function () {
this.filterProperty(
'completed', true
).forEach(this.removeObject, this);
},
My basic test, that I expected to work was this one:
clearAll: function () {
this.forEach(this.removeObject, this);
},
Though, it's leaving some items behind.
If I click the button that calls this function in the Entries controller a couple times the list ends up being empty. I have no clue what's going on! And don't want to do a 'workaround'.
The clearCompleted works perfectly by the way.
The answer depends on what you really want to know-- if you want to clear an ArrayProxy, as per the question title, you just call clear() on the ArrayProxy instance e.g.:
var stuff = ['apple', 'orange', 'banana'];
var ap = Ember.ArrayProxy.create({ content: Ember.A(stuff) });
ap.get('length'); // => 3
ap.clear();
ap.get('length'); // => 0
This way you're not touching the content property directly and any observers are notified (you'll notice on the TodoMVC example that the screen updates if you type Todos.router.entriesController.clear() in the console).
If you're specifically asking about the TodoMVC Ember example you're at the mercy of the quick and dirty "Store" implementation... if you did as above you'll see when you refresh the page the item's return since there is no binding or observing being done between the entry "controller" and the Store (kinda dumb since it's one of Ember's strengths but meh whatev)
Anywho... a "clearAll" method on the entriesController like you were looking for can be done like this:
clearAll: function() {
this.clear();
this.store.findAll().forEach(this.removeObject, this);
}
Well, this worked:
clearAll: function () {
for (var i = this.content.length - 1; i >= 0; i--) {
this.removeObject(this.content[i]);
}
},
If someone can confirm if it's the right way to do it that would be great!