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

Adjustment for scoping #50

Merged
merged 1 commit into from Aug 19, 2014
Merged

Adjustment for scoping #50

merged 1 commit into from Aug 19, 2014

Conversation

giannif
Copy link
Contributor

@giannif giannif commented Aug 12, 2014

The code was wrapped in two anonymous functions, which could cause this to be undefined depending on how the loglevel code was included in a project.

Only using one anonymous function and having this be at the "top-level" allows the code to be wrapped and invoked with apply like so:

var myScope = {}:
(function(){
 // loglevel.js source
 // "this" will equal myScope, since the "this" in loglevel.js is in this scope.
}).apply(myScope);
myScope.log.debug("log was defined on myScope");

The code change follows the same pattern as this:
https://github.com/marionettejs/backbone.marionette/blob/master/src/build/marionette.core.js

I'd love to write a unit test but I'd have to adjust the Gruntfile.js and include grunt-preprocess or grunt-rigger, which are code injection/include tools, to generate a test that includes the loglevel.js wrapped in a function invoked with apply. If you want me to do this I will.

I also hope that my change doesn't affect anything that the undefined argument was doing.
https://github.com/pimterry/loglevel/blob/master/lib/loglevel.js#L8

Thanks,
Gianni

} else {
root.log = definition();
}
}(this, function () {
Copy link
Owner

Choose a reason for hiding this comment

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

I think this should take 'undefined' here as an argument (that's then not provided), because it's easy, and it recreates the previous 'undefined' behaviour.

(This is useful for two reasons: firstly it protects us if somebody very foolishly sets undefined to some actually defined value, and secondly it makes undefined an entirely local variable rather than one potentially used externally, which means it can be minified it to a single character everywhere, which actually saves a reasonable amount of space in the final built copy of the library)

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Passing undefined into line 15 above causes the jasmine:requirejs test to fail. Since the factory is passed to define: define(definition), and it's doing require.js stuff based on the function arguments of definition, one of which being undefined.

If we want, we can do this:

(function(root, definition) {
    if (typeof module === 'object' && module.exports && typeof require === 'function') {
        module.exports = definition();
    } else if (typeof define === 'function' && typeof define.amd === 'object') {
        define(definition);
    } else {
        root.log = definition();
    }
}(this, function() {
    return (function(undefined) {
       // loglevel code....

Up to you.

Copy link
Owner

Choose a reason for hiding this comment

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

Ah, that's annoying. Requirejs trying to be too clever for its own good. Ok, never mind that for now then. I might take a separate look at it later on perhaps, but it doesn't need to block this in the meantime.

@pimterry
Copy link
Owner

Some comments inline, my main concern is the QUnit stuff, otherwise this mostly looks pretty good. I do think it would be nice to have actual tests for this, and I'm totally happy for you to pull in extra grunt plugins to do that, as long as they don't mess with anything that's already in place. Would be good if you could update the 'Setting it up' docs too to cover this new case.

Out of interest, what's the motivation for this? Why do you want to use this without polluting the global context, but also without using an actual module system? Is that a standard pattern in the backbone/marionette world?

@giannif
Copy link
Contributor Author

giannif commented Aug 14, 2014

I have to keep the global context clean since my javascript runs as a third-party plugin. Most module systems seemed to pollute the global context, and I'm not sure there are ways to load modules into a page asynchronously and tell it to use my component's module system versus the one that's defined globally in the page by another developer.

That aside, I like to keep things simple for libraries that are used ubiquitously throughout an app, and just define some vars upfront. For loglevel I would do this:

(function(){
  // My app context
  var log = (function(){ 
   // include loglevel.js
   return this.log;
  )}).apply({});
 // thousands of lines of javascript spread out over many files
 // that use this non-global log directly
})();

This has nothing to do with Backbone.Marionette, it's just a project I've been referencing over the years to get ideas on how to structure my own projects.

I'll work on the test over the weekend. Thanks!

@giannif giannif closed this Aug 14, 2014
@giannif giannif reopened this Aug 14, 2014
@giannif
Copy link
Contributor Author

giannif commented Aug 14, 2014

Sorry I accidentally closed the pull request. Reopened. It's early in California :)

@giannif
Copy link
Contributor Author

giannif commented Aug 17, 2014

I added a test, but put it in a separate branch if you want to look first.
https://github.com/p-js/loglevel/commit/a1d8bd01bbacf18048c3a203ed7513efdd3994df

I generate a test file, run it and then delete the test file.

Just decided to add this too:
https://github.com/p-js/loglevel/commit/1181052c12d4b551a78d7d2947d8e280b914983f

@pimterry
Copy link
Owner

One comment on the tests, but otherwise those look good, you can add them into here and I'll merge.

This allows for more custom integration, enabling a user
to define `this` by wrapping the loglevel code in a function
and invoking it with apply(customContext). Previously,
`this` would be null if invoked with apply.

Fixes #50
@giannif
Copy link
Contributor Author

giannif commented Aug 19, 2014

I added the tests and Travis is now passing. Travis failed earlier due to a PhantomJS crash. Don't think it was code-related.

pimterry added a commit that referenced this pull request Aug 19, 2014
Adjustment for scoping, so loglevel can be used with preprocessing and apply()
@pimterry pimterry merged commit 0327200 into pimterry:master Aug 19, 2014
@pimterry
Copy link
Owner

All looks good, merged. Thanks @giannif!

@giannif
Copy link
Contributor Author

giannif commented Aug 19, 2014

Thanks @pimterry !

I didn't run the dist task though :( So in master dist is out of date. What should we do? Should we bump the version and run dist?

For bower we'd need a new tag too. I'm not sure how you normally do releases.

@pimterry
Copy link
Owner

I've now updated dist and pushed a release, should be up on NPM + Bower + JamJS now, as 1.1.0.

In general most projects don't have you update the dist with each pull request (as far as I've seen), so that was fine. Instead maintainers update it each time they do a new actual release, so that dist always contains the most up to date officially released version, since that's where a lot of people manually downloading releases will pull it from.

(There's actually notes on the release process in the README: https://github.com/pimterry/loglevel#release-process)

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

2 participants