Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Now spies preserve original function arity #1055

Merged
merged 1 commit into from
Sep 28, 2016

Conversation

kapke
Copy link
Contributor

@kapke kapke commented Feb 25, 2016

Resolves #991
Sadly I needed to use eval for make spies working as expected.

@glebec
Copy link

glebec commented Mar 14, 2016

I haven't tested it carefully yet but it looks good to me.

Sadly I needed to use eval for make spies working as expected.

Yes, but as you point out, there isn't much choice for dynamically making spies of a given arity. It would appear for example that Sinon uses hard-coded proxy functions limited to 12 arguments at most and they warn against using eval due to products that use it blocking that keyword. Do we have similar concerns? Which is the lesser of the two evils — limited arity or inclusion of eval? That is perhaps up to the project maintainers to choose.

EDIT: I am going to sleep now but I wonder if something along these lines might help.

More explanation here (look at comments): http://www.bennadel.com/blog/1909-javascript-function-constructor-does-not-create-a-closure.htm
*/
/* jshint evil: true */
wrapper = eval('(function (' + args + ') { return spy.apply(this, Array.prototype.slice.call(arguments)); })'),
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What if we did:

var spy = /* full spy def., which can close over wrapper */,
    wrapper = (function(spy){ return Function(args, 'return spy.apply(this, arguments);') }(spy))

Then you get a "closure" (really just formal param from the IIFE) of the spy, and you also get a working this value.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I was trying with something like that before I used an eval. It looks that when function is created with new Function constructor, then it losts all of context in which it was created. So then I tried passing a spy as first argument, but then I went into troubles with preserving this which resulted the line above.

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Dang. After playing around with it for a while, I now better understand exactly what you mean — Function does not create closures (different from what I call context, which I usually treat as a synonym to the this value). You can sort of force the spy inside the function body if you bind(spy) and use this.apply but then you can't change the binding of the returned spy. Basically it's just not possible to do without eval, as you already figured out. Or you can hard-code in the proxies like Sinon does but then you should add documentation to the effect of "Jasmine spies preserve arity up to length XX but not longer" or similar.

How frustrating.

@glebec
Copy link

glebec commented Mar 14, 2016

Following our discussion, and doing a quick check I can see that eval is already in the Jasmine source, I would say this PR is ready IMHO.

@slackersoft slackersoft merged commit 90dea9a into jasmine:master Sep 28, 2016
slackersoft pushed a commit that referenced this pull request Sep 28, 2016
…mine into kapke-spy-arity-preservation

- Merges #1055 from @kapke
- Fixes #991
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

3 participants