I have a model object defined like this
blog.js
define({title: '', url: '', summary: ''});
I'm using the blog model to hold data from a service and I need to populate an array of blogs. How do I instantiate a new blog within a loop in another module?
I've tried passing in blog as a require object, but it maintains it's reference inside the loop leaving an array of the same object over and over again.
othermodule.js
define(['blog'], function(blog) {
//... more code
$.each(data, function(index, b) {
var tmpBlog = blog;
//...
list.push(tmpBlog);
});
});
I've also tried using var blog = require('blog'); yielding the same result.
So how do I instantiate the blog object using requirejs?
ok, I figured it out. I defined the blog model incorrectly. I needed to return a function blog(){...} instead like so:
define(function() {
return function blog() {
var self = this;
this.title = '';
//...
};
});
and the other module used it like this:
define(['models/blog'], function (model) {
var tmpBlog = new model();
tmpBlog.title = 'some title';
}
Related
I have the following queries, which starts with the GetById method firing up, once that fires up and extracts data from another document, it saves into the race document.
I want to be able to cache the data after I save it for ten minutes. I have taken a look at cacheman library and not sure if it is the right tool for the job. what would be the best way to approach this ?
getById: function(opts,callback) {
var id = opts.action;
var raceData = { };
var self = this;
this.getService().findById(id,function(err,resp) {
if(err)
callback(null);
else {
raceData = resp;
self.getService().getPositions(id, function(err,positions) {
self.savePositions(positions,raceData,callback);
});
}
});
},
savePositions: function(positions,raceData,callback) {
var race = [];
_.each(positions,function(item) {
_.each(item.position,function(el) {
race.push(el);
});
});
raceData.positions = race;
this.getService().modelClass.update({'_id' : raceData._id },{ 'positions' : raceData.positions },callback(raceData));
}
I have recently coded and published a module called Monc. You could find the source code over here. You could find several useful methods to store, delete and retrieve data stored into the memory.
You may use it to cache Mongoose queries using simple nesting as
test.find({}).lean().cache().exec(function(err, docs) {
//docs are fetched into the cache.
});
Otherwise you may need to take a look at the core of Mongoose and override the prototype in order to provide a way to use cacheman as you original suggested.
Create a node module and force it to extend Mongoose as:
monc.hellocache(mongoose, {});
Inside your module you should extend the Mongoose.Query.prototype
exports.hellocache = module.exports.hellocache = function(mongoose, options, Aggregate) {
//require cacheman
var CachemanMemory = require('cacheman-memory');
var cache = new CachemanMemory();
var m = mongoose;
m.execAlter = function(caller, args) {
//do your stuff here
}
m.Query.prototype.exec = function(arg1, arg2) {
return m.execAlter.call(this, 'exec', arguments);
};
})
Take a look at Monc's source code as it may be a good reference on how you may extend and chain Mongoose methods
I will explain with npm redis package which stores key/value pairs in the cache server. keys are queries and redis stores only strings.
we have to make sure that keys are unique and consistent. So key value should store query and also name of the model that you are applying the query.
when you query, inside the mongoose library, there is
function Query(conditions, options, model, collection) {} //constructor function
responsible for query. inside this constructor,
Query.prototype.exec = function exec(op, callback) {}
this function is responsible executing the queries. so we have to manipulate this function and have it execute those tasks:
first check if we have any cached data related to the query
if yes respond to request right away and return
if no we need to respond to request and update our cache and then respond
const redis = require("client");
const redisUrl = "redis://127.0.0.1:6379";
const client = redis.createClient(redisUrl);
const util = require("util");
//client.get does not return promise
client.get = util.promisify(client.get);
const exec = mongoose.Query.prototype.exec;
//mongoose code is written using classical prototype inheritance for setting up objects and classes inside the library.
mongoose.Query.prototype.exec = async function() {
//crate a unique and consistent key
const key = JSON.stringify(
Object.assign({}, this.getQuery(), {
collection: this.mongooseCollection.name
})
);
//see if we have value for key in redis
const cachedValue = await redis.get(key);
//if we do return that as a mongoose model.
//the exec function expects us to return mongoose documents
if (cachedValue) {
const doc = JSON.parse(cacheValue);
return Array.isArray(doc)
? doc.map(d => new this.model(d))
: new this.model(doc);
}
const result = await exec.apply(this, arguments); //now exec function's original task.
client.set(key, JSON.stringify(result),"EX",6000);//it is saved to cache server make sure capital letters EX and time as seconds
};
if we store values as array of objects we need to make sure that each object is individullay converted to mongoose document.
this.model is a method inside the Query constructor and converts object to a mongoose document.
note that if you are storing nested values instead of client.get and client.set, use client.hset and client.hget
Now we monkey patched
Query.prototype.exec
so you do not need to export this function. wherever you have a query operation inside your code, mongoose will execute above code
I'm trying to extend request in order to hijack and enhance its response and other 'body' params. In the end, I want to add some convenience methods for my API:
var myRequest = require('./myRequest');
myRequest.get(function(err, hijackedResponse, rows) {
console.log(hijackedResponse.metadata)
console.log(rows)
console.log(rows.first)
});
According to the Node docs on inherits, I thought I could make it work (and using the EventEmitter example in the docs works OK). I tried getting it to work using #Trott's suggestion but realized that for my use case it's probably not going to work:
// myRequest.js
var inherits = require('util').inherits;
var Request = require("request").Request;
function MyRequest(options) {
Request.call(this, options);
}
inherits(MyRequest, Request);
MyRequest.prototype.pet = function() {
console.log('purr')
}
module.exports = MyRequest;
I've been toying with extend as well, hoping that I could find a way to intercept request's onRequestResponse prototype method, but I'm drawing blanks:
var extend = require('extend'),
request = require("request")
function myResponse() {}
extend(myResponse, request)
// maybe some magic happens here?
module.exports = myResponse
Ended up with:
var extend = require('extend'),
Ok = require('objectkit').Ok
function MyResponse(response) {
var rows = Ok(response.body).getIfExists('rows');
extend(response, {
metadata: extend({}, response.body),
rows: rows
});
response.first = (function() {
return rows[0]
})();
response.last = (function() {
return rows[rows.length - 1] || rows[0]
})();
delete response.metadata.rows
return response;
}
module.exports = MyResponse
Keep in mind in this example, I cheated and wrote it all inside the .get() method. In my final wrapper module, I'm actually taking method as a parameter.
UPDATED to answer the edited question:
Here's a rough template for the contents of your myResponse.js. It only implements get(). But as a bare bones, this-is-how-this-sort-of-thing-can-be-done demo, I hope it gets you going.
var request = require('request');
var myRequest = {};
myRequest.get = function (callback) {
// hardcoding url for demo purposes only
// could easily get it as a function argument, config option, whatever...
request.get('http://www.google.com/', function (error, response, body) {
var rows = [];
// only checking error here but you might want to check the response code as well
if (!error) {
// mess with response here to add metadata. For example...
response.metadata = 'I am awesome';
// convert body to rows however you process that. I'm just hardcoding.
// maybe you'll use JSON.parse() or something.
rows = ['a', 'b', 'c'];
// You can add properties to the array if you want.
rows.first = 'I am first! a a a a';
}
// now fire the callback that the user sent you...
callback(error, response, rows);
});
};
module.exports = myRequest;
ORIGINAL answer:
Looking at the source code for the Request constructor, it requires an options object that in turn requires a uri property.
So you need to specify such an object as the second parameter in your call():
Request.call(this, {uri: 'http://localhost/'});
You likely don't want to hard code uri like that inside the constructor. You probably want the code to look something more like this:
function MyRequest(options) {
Request.call(this, options);
}
...
var myRequest = new MyRequest({uri: 'http://localhost/'});
For your code to work, you will also need to move util.inherits() above the declaration for MyRequest.prototype.pat(). It appears that util.inherits() clobbers any existing prototype methods of the first argument.
Suppose you have a route initialization like this required in your main:
module.exports = function(app) {
for (var name in names) {
var schema = new Schema({}) // schema that accepts anything
, m = mongoose.model(name, schema)
, controller = new TextController(m)
app.get('/path', controller.create.bind(controller))
// etc, etc
And TextController is defined externally as:
var TextController = function(Model) {
this.Model = Model
}
TextController.prototype.create = function(req, res) {
var aDoc = this.Model({ // this is the problematic bit
title: req.body.title
, content: req.body.content})
aDoc.save(function(err) {...})
}
For some reason, mongo saves this as an empty document even though the title and content params are the expected strings. As expected, this.Model is some sort of mongoose object, but it seems to be rejecting the save or the instantiation. Any ideas or suggestions?
Note: I added the controller.method.bind(controller) because it was the only way (I knew of) to get access to this.Model.
Edit: I've also tried the following:
var TextController = function(myCollection) {
this.myCollection = myCollection
this.list = function(req, res) {
this.myCollection.find({}, function { ... })
}
}
And also tried passing in the name and initializing the model within the scope of the function function(name) { this.myCollection = mongoose.model(name) ... }
This turns out to be unrelated to javascript prototypes and completely due to how mongoose does Mixed Type Schemas:
In order to tell mongoose the document has changed you need to markModified(field)
example here: http://mongoosejs.com/docs/schematypes.html#mixed
Is there a way to stub a virtual attribute of a Mongoose Model?
Assume Problem is a model class, and difficulty is a virtual attribute. delete Problem.prototype.difficulty returns false, and the attribute is still there, so I can't replace it with any value I want.
I also tried
var p = new Problem();
delete p.difficulty;
p.difficulty = Problem.INT_EASY;
It didn't work.
Assigning undefined to Problem.prototype.difficulty or using sinon.stub(Problem.prototype, 'difficulty').returns(Problem.INT_EASY);
would throw an exception "TypeError: Cannot read property 'scope' of undefined", while doing
var p = new Problem();
sinon.stub(p, 'difficulty').returns(Problem.INT_EASY);
would throw an error "TypeError: Attempted to wrap string property difficulty as function".
I am running out of ideas. Help me out! Thanks!
mongoose internally uses Object.defineProperty for all properties. Since they are defined as non-configurable, you can't delete them, and you can't re-configure them, either.
What you can do, though, is overwriting the model’s get and set methods, which are used to get and set any property:
var p = new Problem();
p.get = function (path, type) {
if (path === 'difficulty') {
return Problem.INT_EASY;
}
return Problem.prototype.get.apply(this, arguments);
};
Or, a complete example using sinon.js:
var mongoose = require('mongoose');
var sinon = require('sinon');
var problemSchema = new mongoose.Schema({});
problemSchema.virtual('difficulty').get(function () {
return Problem.INT_HARD;
});
var Problem = mongoose.model('Problem', problemSchema);
Problem.INT_EASY = 1;
Problem.INT_HARD = 2;
var p = new Problem();
console.log(p.difficulty);
sinon.stub(p, 'get').withArgs('difficulty').returns(Problem.INT_EASY);
console.log(p.difficulty);
As of the end of 2017 and the current Sinon version, stubbing only some of the arguments (e.g. only virtuals on mongoose models) can be achieved in the following manner
const ingr = new Model.ingredientModel({
productId: new ObjectID(),
});
// memorizing the original function + binding to the context
const getOrigin = ingr.get.bind(ingr);
const getStub = S.stub(ingr, 'get').callsFake(key => {
// stubbing ingr.$product virtual
if (key === '$product') {
return { nutrition: productNutrition };
}
// stubbing ingr.qty
else if (key === 'qty') {
return { numericAmount: 0.5 };
}
// otherwise return the original
else {
return getOrigin(key);
}
});
The solution is inspired by a bunch of different advises, including one by #Adrian Heine
I am working on a node.js application with postgresql, using the express framework. I am trying to follow MVC as much as possible.
I want to generate query results in a model class and then pass them to a controller class. That controller class is actually defined in the routes, so that controller class can take the results and pass them as http response.
This is my database helper class, i.e. the model class. My problem is at the listener at the very end of the class.
exports.DatabaseHelper = function()
{
var allVenues;
var client;
var customEventEmitter;
this.init = function()
{
this.customEventEmitter = new events.EventEmitter();
client = new pg.Client(
{
host:'localhost',
port:5432,
database:'postgres',
user:'postgres',
password:'password'
});
}
this.getVenuesWithEvents = function(searchParams)
{
allVenues = new Array();
var query_for_venues;
this.init();
client.connect();
client.addListener("error",function()
{
sys.puts("postgresql interface error");
});
query_for_venues = client.query("select id, name, location, latitude, longitude, category_generalized from venues");
query_for_venues.addListener("row",function(row)
{
//some code
});
query_for_venues.addListener("end",function(result)
{
this.customEventEmitter.emit("data",allVenues);
//////////////////////////////////////////////////////////
//this line shows error....'this' refers to the query object so customEventEmitter is undefined
//customEventEmitter is my idea of sharing the query results to my controller class.
//but I cannot do this becasue of this error
console.log("after emission");
});
}
}
How can I access the customEventEmitter instance variable from within the listener?
Just remove this from your init function:
this.customEventEmitter = new events.EventEmitter();
So you'll have:
customEventEmitter = new events.EventEmitter();
And in your listener just emit the emitter without this as follows:
query_for_venues.addListener("end",function(result){
customEventEmitter.emit("data",allVenues);
console.log("after emission");
});
let me show you a nice trick.
you could change custom
var customEventEmitter;
to
this.customEventEmitter =null;
at the top of the function. then you can call
var self = this;
outside of the query function. then inside the query function you reference the outer "this" with self.
as in:
self.customEventEmitter.emit()
the methodology I just described is standard.