I'm testing some routines that Meteor has and I would like to know if this is it's normal activity or I have something wrong...
I will describe step by step:
I publish my collection in order to have only what I want
server/publish.js
Meteor.publish('decisions', function (decisionCursor) {
return Decisions.find({ active: true }, { limit: 20, skip: decisionCursor });
});
Meteor.publish('decisionsToModerate', function (decisionCursor) {
return Decisions.find({ active: false }, { sort: { createdAt: -1 }, limit: 1, skip: decisionCursor });
});
I subscribe my client to both collection publications
client/client.js
Meteor.startup(function () {
SimpleSchema.debug = true;
Session.setDefault('decisionCursor', 0);
Deps.autorun(function () {
Meteor.subscribe("decisions", Number(Session.get('decisionCursor')));
Meteor.subscribe("decisionsToModerate", Number(Session.get('decisionCursor')));
});
});
I set up functions to retrieve both collections in order to prevent calling a query everytime...
client/lib/environment.js
activeDecisions = function() {
var decisions = Decisions.find({active: true});
console.log(decisions.fetch().length); // PROBLEM HERE
return decisions;
};
moderateDecisions = function() {
return Decisions.find({active: false});
};
I create my view stuff
client/views/home/home.js
Template.home.activeDecisions = function() {
var decisions = activeDecisions();
return decisions;
};
As you can see, on client/lib/environment.js I have added a line for you to see where I see the problem...
When I go to http://localhost:3000/ iron-routes loads
this.route('home', {
path: '/',
layoutTemplate: 'masterLayout'
});
If I got o Chrome Console, as I wrote on client/lib/environment.js it should return a line with a number of Decisions documents, in this case I only have 3 active Decisions, but Chrome outputs this:
0 environment.js?9868bbbef2024c202fd33213ed060f067dadbe75:3
3 environment.js?9868bbbef2024c202fd33213ed060f067dadbe75:3
3 environment.js?9868bbbef2024c202fd33213ed060f067dadbe75:3
Three lines, first one tells me that I have 0 documents (what? I have THREE active documents) next two lines tells me exactly what I wanted to know, that I have three documents.
I want this number because I want to set it in a session variable that will update every time that query is called because if I set it in other place (let's say for example Template.home.rendered) I will make TWO querys and that will be slower.
So the problem I see is that I don't know why Meteor writes three times into console if I told it to write it only once, when query is parsed to a variable... If I set session it will be 0, then 3 and then 3... That may cause some bugs?
Template helpers form a reactive context - if reactive variables inside of them get updated, the helper will run again. So whenever the cursor returned by activeDecisions gets updated you will see that line get printed to the console.
It's perfectly reasonable for the function to print 0 when the template is first rendered. Remember that the documents you subscribed to may not have arrived on the client before the template is rendered. As new documents arrive or get updated, activeDecisions will be evaluated again. For more details see my blog post on a similar subject.
Going back to your original question, you can set a session variable to the cursor count (BTW it's more efficient to call cursor.count() than cursor.fetch().length). When that count gets updated, so will your session variable. Because session variables are reactive, all of its dependencies will be rerun again and so on.
Related
I am having a hard time trying to adjust to asynchronous using node.js. I ran into an issue when using selenium-webdriver and the page object pattern. I feel like somethings have to be synchronous when doing automation testing or your tests will fail because you clicked a button before inserting data. I am having an issue similar to this. I want to add an employee and then search for the employee, but the search for employee is performing before add employee.
var employee = new Employee('grimlek', 'Charles', 'Sexton', 'TitleTitle',
'Upper Management', 'Company Admin', 'Contractor', '-7', 'Remote',
'05212016', '3369407787', '3368791234', 'charles#example.com',
'charles.sexton', 'Skype', 'abcdefgh');
driver.get('https://website.com/login')
.then(function() {
//This behaves as intended
loginPage.login('company.admin', 'password') })
.then(function() {
//Add employee
employeePage.addEmployee(employee) })
.then(function() {
//Search for employee after employee is added
employeePage.searchEmployee(employee)});
EmployeePage Object
var EmployeePage = function (driver) {
this.addEmployee = function (employee) {
driver.findElement(webdriver.By.css('button[class=\'btn btn-default\']')).then(function (element) {
//
//Search employee function is done before the line below this
//
element.click();
}).then(function () {
setTimeout(function () {
driver.findElement(webdriver.By.id('employee_username')).then(function (element) {
element.sendKeys(employee.username);
});
driver.findElement(webdriver.By.id('employee_first_name')).then(function (element) {
element.sendKeys(employee.firstName);
});
driver.findElement(webdriver.By.id('employee_last_name')).then(function (element) {
element.sendKeys(employee.lastName);
});
driver.findElement(webdriver.By.id('employee_title_id')).then(function (element) {
element.sendKeys(employee.title);
});
driver.findElement(webdriver.By.id('employee_role')).then(function (element) {
element.sendKeys(employee.role);
});
}, 5000);
});
//
//
//Search employee should occur when the thread leaves the function
//
};
this.searchEmployee = function (employee) {
driver.findElement(webdriver.By.css('input[class=\'form-control ng-pristine ng-valid\']')).then(function(element) {
element.sendKeys(employee.firstName + ' ' + employee.lastName);
});
};
};
module.exports = EmployeePage;
I know that both searchEmployee and addEmployee functions don't return a promise and I am trying to chain them with the .then function. I do believe this is sorta my problem but I need help with how it should be done and not how I can rig it. Should I use callbacks? I have worked on this problem for going on four hours now and I have tried googling and doing research on various topics. If I didn't provide enough code please let me know and I will provide a simplified runnable example.
A laudable goal is to make each test independent. If a change is made to the application (e,g, bug fix) only the impacted test(s) need to be executed. Also, it makes moving to grid thinkable.
But this is difficult to achieve in practice. Your test has to include all tests needed to satisfy the prerequisites.
Cucumber has feature files that include scenarios Each scenario is a test. Scenarios are executed in the order they are listed in the feature file. So one way to organize things is to include all the prerequisite scenarios before your test in a feature file, You can add tag(s) before the Feature statement so that when you execute that tag the entire feature file runs. Perhaps the first scenario resets (a subset of) the database to a know state.
The trick would be to run features in parallel on multiple machines. If you point those multiple clients to the same server beware that the features should not create or update overlapping entities that could collide when written to the database by the server. E.g. "What do you mean that user 'tom' already exists?" Each feature needs to create a unique user name.
The way of approach using cucumber is to divide you steps for every individual operation.
Ex:
Given I am on XYZ Form
And I provide all form details
In above case, for step And I provide all form details you will be including all the fields in step definition and start filling the fields say name, last name, address in single step definition.
Instead of this we should divide the step for every individual field like:
Given I am on XYZ Form
And I provide name details in XYZ Form
And I provide last name details in XYZ Form
And I provide address details in XYZ Form
And then we will be writing 3 step definition which of course will be running sequentially.
You may feel that the typing work got increased and step definitions got increased unnecessarily, but this will actually help you when a field gets removed from the application itself, you will be only needing to delete related step from future file.
More over you can easily test validation for fields by just commenting one of the step in your feature file.
And your code will be more easy to maintain as every steps is working independently.
And of course sequential work will get achieved.
I have some collections which are related to others through an ID.
For instance, I have collections Post and Comments. I want to display the number of comments to each posts. Therefore, I have a field in Post called numComments. I could update this number in a method every time a comment with same postId is either inserted og removed but I will instead use some hooks/observers to ensure the number is always updated.
Therefore, I have created a file server/observers.js with content
Comments.find().observe({
added: function(document) {
Posts.update({ postId: document.postId }, { $inc: { numComments: 1 } });
},
changed: function(document) {
},
removed: function(document) {
Posts.update({ postId: document.postId }, { $inc: { numComments: -1 } });
},
});
I like this kind of solution but is it a good way to do it?
My problem is that since I implemented this functionality, the console window prints an awful lot of errors/warnings. I suspect it is because of the observers.
In the documentation (http://docs.meteor.com/#/full/observe), it says:
observe returns a live query handle, which is an object with a stop method. Call stop with no arguments to stop calling the callback functions and tear down the query. The query will run forever until you call this (..)
I am not sure what it means but I think the observers should be stopped manually.
Have a look at this answer. It might lead you in the right direction, since the example is very similar to what you want. You don't need a dedicated field in your collection to get a reactive counting of your comments, you can build it in your publish function.
I am not sure what it means but I think the observers should be
stopped manually
You're right. In the example linked above, the query is wrapped inside a handle variable. Notice the
self.onStop(function () {
handle.stop();
});
It allows you to make sure that no observers will still be running once you stop publishing.
I'm using Parse cloud code to update some counters on a user when after_delete is called on certain classes. A user has counters for subscriptions, followers and following that are incremented in the before_save for subscriptions and follows and decremented in the before_delete for the same classes.
The issue I'm running into is when a user is deleted. The after_delete function destroys all related subscriptions/follows, but this triggers an update to the (deleted) user via before_delete for subscriptions/follows. This always causes the before_delete to error out.
Perhaps I'm conceptually mixed up on the best way to accomplish this, but I can't figure out how to properly set up the following code in follow before_delete:
var fromUserPointer = follow.get("fromUser");
var toUserPointer = follow.get("toUser");
fromUserPointer.fetch().then( function(fromUser){
// update following counter
// if from user is already deleted, none of the rest of the promise chain is executed
}.then( function (fromUser){
return toUserPointer.fetch();
}.then( function(toUser){
// update followers count
}
Is there a way to determine if the fromUserPointer and toUserPointer point to a valid object short of actually performing the fetch?
Its not an error to not find the user, but by not handling the missing object case on the fetch, its being treating implicitly as an error.
So...
fromUserPointer.fetch().then(f(result) {
// good stuff
}).then(f(result) {
// good stuff
}).then(f(result) {
// good stuff
}, f(error) {
// this is good stuff too, if there's no mode of failure
// above that would cause you to want NOT to delete, then...
response.success();
});
I have a collection called "Materials" defined on the client and server. In one template, I can insert and update correctly to Materials. On another template, I can insert and update but when I refresh the browser, the changes are lost.
The two views are called
view_materials (inserting/updating works)
view_orders (doesn't work)
Both templates have the materials collection binded to them like so:
//Bind viewOrders to Materials collection
Template.view_order.materials = function () {
return Materials.find();
};
// Bind materialsTemplate to Materials collection
Template.view_materials.materials = function () {
return Materials.find();
};
and both are using the function below to update.
var docid = Materials.findOne({material_number: newMaterial.material_number});
console.log(docid._id);
Materials.update({_id:docid._id},{$set: {material_qty: total}});
Please note that the ID printed out to the console matches perfectly. Like I mentioned, on view_orders it updates for a moment on the client but not in another browser window nor does it persist after being reloaded from the server. On view_materials it works perfectly. Any ideas?
I also deployed an example of the bug here:
http://upexmple.meteor.com/
and added the source to github:
https://github.com/stereochromatic/update_example
The relevant code can be found in client/views/view_materials/view_materials.js
and
client/views/view_orders/view_orders.js
To duplicate the error:
Click on inventory and under Raw Materials, type A for material number and -50 for quantity. You will see it get updated correctly. Now click on create release and under raw material type, select A and -50 for quantity. You will see the correct info get printed to the console and you may also see the changes on inventory but upon refresh those changes are gone.
- show quoted text -
You need to define your Meteor Collections outside of the client directory. I usually put mine into a /lib folder, but defining collections on just the client side can cause issues.
So just move
Materials = new Meteor.Collection("materials");
and
MaterialsLog = new Meteor.Collection("materialsLog");
into a folder outside of the client/server folders.
I can't reproduce the error. I've done as you said, but the changes do not disappear for me upon refresh. I can even close the tab and reopen it, and the changes are still there.
Finally figured this out...
I removed the autopublish package with
meteor remove autopublish
and then defined my permissions (and subscriptions) in /lib/Materials.js like so:
// Declare client materials collection
Materials = new Meteor.Collection("materials");
materials = Materials
if (Meteor.isClient) {
Meteor.subscribe('materials');
}
if (Meteor.isServer) {
Meteor.publish('materials', function() {
return materials.find();
});
materials.allow({
insert: function (document) {
return true;
},
update: function () {
return true;
},
remove: function () {
return false;
}
});
}
Thanks to everyone who helped out. I realize that for a production app this is best practice anyways so this needed really to be done at some point. I originally was going to wait until the end to remove autopublish, insecure and wrap collection in publish/subscribe, allow/deny rules but this issue helped expedite that work :)
Using your code above my with file structure:
Please take note that I remove the materials = Materials
client folder:
subscriber.js
Meteor.subscribe('materials');
server folder
publisher.js
Meteor.publish('materials', function() {
return Materials.find();
});
allow.js
Materials.allow({
insert: function (document) {
return true;
},
update: function () {
return true;
},
remove: function () {
return false;
}
});
}
collection folder. this is outside from the client and the server folders
collections.js
Materials = new Meteor.Collection("materials");
let me know if its works and though this very late at least people can see it
While developing code for a online calculator framework called candy-calc, I have two ko.observables called calcVar1.selUnit and calcVar2.selUnit which I want to connect together. By that I mean, if one changes, the other one will change, and vise versa. The way I have tried to go about this is to create two display variables calcVar1.dispSelUnit and calcVar2.dispSelUnitwhich are ko.computed(). These are bound to in the view, and they have different read/write functions as follows.
// Modified write function
calcVar1.dispSelUnit = ko.computed({
read : function(){
console.log('Reading source var sel unit.');
// Return as usual
return calcVar1.selUnit();
},
write : function(value){
// Write to both of them
console.log('Writing ' + value + 'to both sel unit.');
calcVar1.selUnit(value);
calcVar2.selUnit(value);
},
owner : this
});
// Modified write function
calcVar2.dispSelUnit = ko.computed({
read : function(){
console.log('Reading destination var sel unit.');
// Make it equal to the source variables selected unit
return calcVar2.selUnit();
},
write : function(value){
// Write to both of them
console.log('Writing ' + value + 'to both sel unit.');
calcVar1.selUnit(value);
calcVar2.selUnit(value);
},
owner : this
});
}
So basically, the dispSelUnits are acting as a intermediary to the real selUnit values below, and on write update both selUnit (ko.observables), while on read behave as normal.
I can't see anything wrong with this logic. However, when running this, if I try and update compVar1.dispSelUnit, it enters a infinite loop where compVar1.dispSelUnit gets written then read, and then compVar2.dispSelUnit gets written and read, and then back again.
We also discussed this on Github (https://github.com/mbest/knockout-deferred-updates/issues/17). After looking through his code, I made the following observation and suggestion.
You're getting the recursion problem because the two variables have value bindings to select boxes with different list of units. Although they display the same units, they are actually different objects. The value binding always tries to set the observable to the currently selected item in the list. But because the lists are different, this is really impossible, and the observables switch endlessly between the two values.
To fix this, you need both select boxes to reference the same objects. In standard-resistance-finder.js, do this:
var resistenceUnits = [
new cc.unit('m\u2126', 0.001),
new cc.unit('\u2126', 1.0),
new cc.unit('k\u2126', 1000.0)
];
this.desiredRes = new cc.input(
this,
function() { return true; },
resistenceUnits,
0);
...
this.actualRes = new cc.output(
this,
...
resistenceUnits,
0, 2);
Regarding the question of keeping two observables in sync, this question might provide some answers: Simple, clean way to sync observables from different view models