I'm adding an instance method to a sequelize model. According to the documentation I should be able to reference this.name, but the only value I can see is this.dataValues.name. I have no reason to believe that the high quality documentation is wrong .... but why does this happen?
Also, there are no setters or getters available. this.getDataValue / this.setDataValue work in getters / setters, but not in instanceMethods.
I can't find any relevant samples on the net - if you know of a project that reads (or better, writes) these values, please add that to your response.
module.exports = (sequelize: Sequelize, DataTypes: DataTypes) => {
return sequelize.define<UserInstance, UserPojo>('User', {
name: { type: DataTypes.STRING }
}, {
instanceMethods: {
myMethod: (value) => {
// this.name is undefined
// this.dataValues.name IS defined ??
}
}, ...
As you can probably see, I'm using Typescript. I just examined the generated Javascript, and immediately saw the problem.
Typescript puts this at the top of the module:
var _this = this;
And references that '_this', rather than the one in the context of the function -- didn't realize that! As soon as I changed this to traditional function() { } syntax, it worked. So, if you're using typescript, you can do this:
myMethod: function(value: type) => void {
}
That is, you don't have to give up on typing your arguments and return value.
Related
I'm using the Uppy Vue component library, and following the docs, I've initialized Uppy by adding it as a computed property.
computed: {
uppy: () => new Uppy({
logger: Uppy.debugLogger
}).use(AwsS3Multipart, {
limit: 4,
companionUrl: '/',
}).on('complete', (result) => {
this.testing = 'success';
console.log('successful files:', result.successful);
console.log('failed files:', result.failed);
}),
}
I'm trying to update my Vue component's data now by using Uppy's complete event, but "this" is not defined. I'm not quite sure how to access "this" from here.
Any idea how to go about doing this?
Update
After posting this, I found a solution that works. I'm hesitant with this solution though as it seemed too easy.
If no one provides a better solution, I'll add this as the answer.
// Uppy Instance
uppy: function() {
return new Uppy({
logger: Uppy.debugLogger
}).use(AwsS3Multipart, {
limit: 4,
companionUrl: '/',
}).on('complete', (result) => {
this.testing = 'success';
console.log('successful files:', result.successful);
console.log('failed files:', result.failed);
})
},
By following the Uppy docs and instantiating the Uppy instance with an arrow function, this no longer seems to refer to the Vue. This makes it so that accessing this.method(), or this.variable, etc. no longer works.
My solution was to change the Uppy instantiation from an arrow function to a regular function. I believe this causes this to refer to the global instance, but I don't have a solid understanding of this, so take what I say with a grain of salt.
I changed this:
computed: {
uppy: () => new Uppy()
}
To this:
computed: {
uppy: function() { return new Uppy() }
}
I'm creating a blog in my nuxt-app that pulls data from contentful ive been following this tutorial, now I can get that all right, but I cant seem to get both context and the environment variables I set up to return from the asyncData argument
I have created a json file like so..
.contentful.json
{
"CTF_BLOG_POST_ITEM": "...",
"CTF_BLOG_POST": "...",
"CTF_SPACE_ID": "...",
"CTF_CDA_ACCESS_TOKEN":"..."
}
and then in my nuxt.config.js
env: {
CTF_SPACE_ID: config.CTF_SPACE_ID,
CTF_CDA_ACCESS_TOKEN: config.CTF_CDA_ACCESS_TOKEN,
CTF_BLOG_POST_ITEM: config.CTF_BLOG_POST_ITEM,
CTF_BLOG_POST: config.CTF_BLOG_POST
}
now basically in my component I've been trying to do this
asyncData(context, {env}) {
return Promise.all([
client.getEntries({
'content_type': env.CTF_BLOG_POST_ITEM,
order: '-sys.createdAt'
})
]).then(([posts]) => {
console.log(context);
return {
posts: posts.items
}
})
},
but when I run this I get cannot read property CTF_BLOG_POST_ITEM of undefined, if I take context out of the arguments this works, and vice versa if I take the {env} I get the context.
How can I get both??
Thanks
The primary (1st) argument of asyncData() is the context object. env is a property of the context object. You could access it as context.env without the use of object restructuring assignment. Your example could be rewritten in the following way without the use of object restructuring assignment:
asyncData(context) {
return Promise.all([
client.getEntries({
'content_type': context.env.CTF_BLOG_POST_ITEM,
order: '-sys.createdAt'
})
]).then(([posts]) => {
console.log(context);
console.log(context.env);
return {
posts: posts.items
}
})
},
The signature asyncData(context, {env}) { is incorrect because you are adding a second argument, {env}, which does not reference the aforementioned context object in any way. If you only need env from context, you can use object restructuring assignment to extract this property in the following way (as you mentioned works when you remove the 1st argument:
asyncData({env}) {
return Promise.all([
client.getEntries({
'content_type': context.env.CTF_BLOG_POST_ITEM,
order: '-sys.createdAt'
})
]).then(([posts]) => {
console.log(context);
console.log(context.env);
return {
posts: posts.items
}
})
},
If you need additional context object properties, using object destructuring assignment. Tt would look like:
asyncData({env, params, req, res}) {
Otherwise, you can just access properties such as context.env, context.params, etc by simply passing context as the first/primary argument with restructuring.
Hopefully that helps!
Given the (overly simplified) snippet:
import Validator from 'validator';
export default function isValid(arg) {
// Validator#isValid is an ES6 getter
return new Validator(arg).isValid;
}
How can I test that a Validator was instantiated with the given parameter? And stub isValid?
I know I can restructure my code to avoid the issue, I am not looking for a workaround as I found plenty (dependency injection, not using ES6 sugar, etc.).
I found a way, but it is terribly ugly. In test file:
import ValidatorNamespace from 'validator';
const Validator = ValidatorNamespace.default;
let validatorInstance;
let validatorConstructor;
const subject = arg => isValid(arg);
const validityStatus = true;
describe('isValid helper', () => {
beforeEach(() => {
validatorInstance = sinon.createStubInstance(Validator);
// Yay! This is how I managed to spy on the constructor!.. :(
validatorConstructor = sandbox.stub(ValidatorNamespace, 'default').
returns(validatorInstance);
sandbox.stub(validatorInstance, 'isValid').value(validityStatus);
});
it('instantiates the validator properly', ()=> {
subject('arg');
expect(validatorConstructor).to.have.been.calledWith('arg')
});
it('returns the value returned by the validator', ()=> {
expect(subject('arg')).to.eq(validityStatus);
});
});
Validator code:
export default class Validator {
constructor(arg) {
this.innerValue = arg;
}
get isValid() {
return aFunctionOf(this.innerValue);
}
}
What you want isn't really possible. Stubbing requires some kind of "seam" through which to put the stubs in place. When you import functions (constructors or otherwise) directly in your production code, the only seam you're leaving is the import process itself.
There is proxyquire, which overrides require calls in node. I don't know what environment you're using, and I don't really know how well this plays with ES6 modules. If you're transpiling to ES6 using babel, though, it should work.
In my experience this kind of stuff is not worth the additional complexity. My usual workaround is to just make a static factory function and stub/use that instead of using the constructor directly:
export default class Validator {
constructor(arg) {
this.innerValue = arg;
}
static create(arg) {
return new Validator(arg);
}
get isValid() {
return aFunctionOf(this.innerValue);
}
}
If you want a unit test for the factory, you can simply check the returned instance instead of stubbing the constructor:
it('create should return an instance', function() {
let arg = { foo: 'bar' };
let result = Validator.create(arg);
expect(result).to.be.an.instanceof(Validator);
expect(result.innerValue).to.equal(arg);
});
I am following my school's workshop regarding how to integrate Sequelize with Express. There is a section where we are learning to leverage hooks in our models—and in it I was confused by this:
Returning vs. Attaching
A hook runs with the instance of a Page being
saved given as an argument. We want to, therefore, attach a created
urlTitle to this page instance instead of returning it from the
function.
var Sequelize = require('sequelize');
var db = new Sequelize('postgres://localhost:5432/__wikistack__', {
logging: false,
});
const Page = db.define(
'page',
{
title: {
type: Sequelize.STRING,
},
urlTitle: {
type: Sequelize.STRING,
},
content: {
type: Sequelize.TEXT,
},
status: {
type: Sequelize.ENUM('open', 'closed'),
},
},
{
hooks: {
beforeValidate: function(page) {
if (page.title) {
// Removes all non-alphanumeric characters from title
// And make whitespace underscore
return (page.urlTitle = page.title.replace(/\s/g, '_').replace(/\W/g, ''));
} else {
// Generates random 5 letter string
return (urlTitle = Math.random()
.toString(36)
.substring(2, 7));
}
},
},
}
);
Can someone explain this? How can the function in the hook not return something? The above works, so the hook/function is returning something.
Thanks in advance!
Hooks are just code that gets run at certain life cycle points of a record instance. You can have them be purely side effects. In your case, all you need to do is modify the page object that the hook is passed, return doesn't help or hurt.
However, the return value of a hook is not useless. If you need to do anything async inside a hook, you have to return a promise.
Also asked in official Restify repo: #1224
Hi,
Is it possible to have one default formatter that can handle any accept type that is not defined.
For Example:
restify.createServer({
formatters: {
'application/json': () => {},
// All other requests that come in are handled by this, instead of throwing error
'application/every-thing-else': () => {}
}
});
By all appearances, this is impossible. Since the formatters are stored in a dictionary, there is no way to create a key that matches every input (that would kind of defeat the point of a dictionary anyway...) The only way to accomplish this kind of thing outside of JSON would be with a regular expression, and regular expressions don't work with JSON.
Here is a program I wrote to test this.
var restify = require("restify");
var server = restify.createServer({
formatters: {
'application/json': () => { console.log("JSON") },
"[\w\W]*": () => { console.log("Everything else") } // Does not work
}
});
server.get("/", (req, res, next) => {
console.log("Root");
res.setHeader("Content-Type", "not/supported");
res.send(200, {"message": "this is a test"});
next()
});
server.listen(10000);
Also here is a link to the documentation on this in case you can find some hint that I couldn't see.
Restify documentation