nodemon does not watch node_modules

I'm no fan of the node_modules structure. I'd have gone for a dichotomy, node_library and node_modules. node_library would be the place that npm and yarn install stuff from npmjs.org. node_modules would be for my modules and other code. I would have node search up the tree in both folders. Complicated, yes. But having no systematic way of determining which code is mine is simply awful.

In one of the goofiest decisions in the node world, the project monitor, nodemon, does not look inside node_modules to figure out whether to restart a project when files are changed. This is certainly because it has to monitor a zillion files if it does and the guy worries about performance.

The problem is that my projects are comprised of node modules and they are put in node_modules. If there is a better place to put them, I beg you, write some comments and save me and the rest of the misery.

So, if you

cannot get nodemon to restart your project or
nodemon won't detect changed files
nodemon will not watch node_modules
(put search phrases in the comments, please)

you can remedy the situation by adding an ignoreRoot key to you nodemon.json file
{
  "ignoreRoot": [".git", ".jpg", ".whatever"]
}


This overrides the default ignore behavior entirely. Choosing to not list node_modules means they can now be watched.

This is, in fact, explained on the github site (here) but, you have to read a lot of stuff to get to it and it doesn't get found by google.

Perhaps this will change that.

Scrolls Bars on Macintosh

Among the worst things that Apple ever did was make it so that scroll bars are only visible when you want to use them. Often it's hard to figure out how to activate them or they go away before I'm done using them.

Of course, Apple is actually awesome and, after all these years, I just realized that they provide make them show all the time. In the two days since I discovered this, I am happy again for the first time.

To accomplish this minor miracle...

System Preferences -> General -> Show Scroll Bars -> Always

It's like being able to breathe again.

rsync error: error in rsync protocol data stream (code 12)

The internet failed to tell me:

This error can result from not having one of the directories present. Yes, I know that rsync creates lots of directories very nicely. Not all of them.

To wit:

rsync someDirectory someUser@1.1.1.1:/home/someUser/system/code

Gave the the data stream error until I created the directory 'system'.

I can't imagine why. The only thing that is distinctive about 'system' is that it is also ~/system, ie, at the top of the user's directory.



Windows is Bad, Example #billion

I often note that whenever Microsoft has a decision to make, they unerringly choose the worst alternative. Most of these are so small that I forget about them but today, I ran into one that is so perfect, I want to remember it forever.

As with my Macintosh, one way to rename a file is to click once directly on the text of the name. It will turn into a text entry and select the name, awaiting the new file name.

Occasionally, I want to append something to the name, eg, BACKUP, HOLD, TMP, DISCARD, etc. So that I can use the real file name for something new without having to lose access to the old file.

On my Macintosh, I open the name for editing and touch the right arrow. This moves the cursor to the end of the selection and lets me keep typing.

On my Windows machine, it moves it past the end of the selection and then past the period so that I am changing the file extension. While it's not impossible that this is what I want (on my Mac, select-all, right-arrow, delete delete delete), it's relatively rare.

Which is to say, Windows forces me to do extra work to accomplish the most common function. Like always. It also increases the likelihood of error.

How much does Windows suck? Just sayin'.

Access Web.config from Classic ASP

I am in pain. The things I do for money. Visual Basic in asp. Kill me now!

I am handed this app with the URL of a JSON endpoint hard coded into it. That might not be horrible except that this is not a one-shot application. This is something we sell to a lot of people and the endpoint needs to point to the customer's domain or that nasty cross browser stuff will bite you.

After sneering about the laziness, I set about looking for the equivalent of getEnv() or process.env.varName or any of the sane ways that other language systems provide access to configuration data.

Nope. This is Microsoft after all. Not only can't I find said simple 'read the config' function, I can't really find a straight answer. (The fact that I didn't know to google "Classic ASP" didn't help. - Hey! Stop laughing. I still am not sure what the language is called. vbscript, visual basic, asp... WTF? I hate Microsoft apps. I'm only doing this because nobody else in my shop could figure out why it was broken.)

Of course, StackOverflow eventually came to the rescue, sort of. A nice person named Connor worked through this problem. It didn't work for me right away but, thanks. It saved my bacon. I present it here so I can find it again if I am ever again unable to escape a Classic ASP task, and so that it has more good google keywords. This is copied directly out of my app. It works.


'****************************** GetConfigValue *******************************
' Purpose:      Utility function to get value from a configuration file.
' Conditions:   CONFIG_FILE_PATH must be refer to a valid XML file
' Input:        sectionName - a section in the file, eg, appSettings
'               attrName - refers to the "key" attribute of an entry
' Output:       A string containing the value of the appropriate entry
'**********************************************************************************

CONFIG_FILE_PATH="Web.config" 'if no qualifier, refers to this directory. can point elsewhere.
Function GetConfigValue(sectionName, attrName)
    Dim oXML, oNode, oChild, oAttr, dsn
    Set oXML=Server.CreateObject("Microsoft.XMLDOM")
    oXML.Async = "false"
    oXML.Load(Server.MapPath(CONFIG_FILE_PATH))
    Set oNode = oXML.GetElementsByTagName(sectionName).Item(0)
    Set oChild = oNode.GetElementsByTagName("add")
    ' Get the first match
    For Each oAttr in oChild
        If  oAttr.getAttribute("key") = attrName then
            dsn = oAttr.getAttribute("value")
            GetConfigValue = dsn
            Exit Function
        End If
    Next
End Function

settingValue = GetConfigValue("appSettings", "someKeyName")
Response.Write(settingValue)


ps, here's Connor's post: http://stackoverflow.com/questions/28960446/having-classic-asp-read-in-a-key-from-appsettings-in-a-web-config-file/34779392#34779392



why use "console.log.bind(console)"

Oddly, I never saw this construct much until recently (late 2015). I suppose that's because the problem it solves wasn't common before promises arrived on the scene and people wanted to pass console.log around for debugging purposes. When it did start showing up, I was perplexed. I am 100% comfortable with the details of the bind() function and why to use it. I could not, for the life of me, understand why it was needed here.

The answer:

It's not needed. I tried to get the behavior discussed below in a few contexts and it appears that console.log has been revised so that this isn't needed. Or, maybe it is. I didn't do a comprehensive check of the entire world.

Well, then, wtf?

Turns out that, in some previous world (perhaps some browsers today that I don't have the patience to experiment with), console.log() contains a reference to it's own this which is lost in a simple function assignment. IE...

If you, for some reason, want to pass the function console.log around, eg,

var xxx=console.log

xxx('message');

It won't work. The console.s log() method refers to this.something that isn't a part of it's new, post assignment 'this' (ie, the global scope, which is 'this' when nothing else is applicable).

That is, if you could magically add console.dir(this) to console.log, you would get something in this spirit...

{

log:function,

somethingElse:godKnowsWhat

}

But, if you were magically able to get that same console.dir(this) after the assignment to xxx, it would produce something like this...

{

//global scope items that do not include anything name somethingElse

}

The construct in question specifies the value of this for the newly assigned xxx

var xxx=console.log.bind(console);

That is, it will be bound to its original 'this', (ie, this===console) and it will be able to find whatever somethingElse it needs.

Mongo Mongoose Objects Weird Properties Can't Access

Yep. I'm all about search engine titles this week.

In my new Mongo/mongoose world, I've exposed myself ot a ton of information, tutorials, explanations, examples, you name it and there is a HUGE thing that no one mentions.

Perhaps I'm an idiot or maybe the fact that I work entirely alone means that I am not privy to common knowledge so common that no one needs to say it but, I'm saying it now:

Mongoose does not create nice Javascript objects. When you do a find, you get something that will console.dir() and look like something you recognize. It is a false appearance. It is some nasty Mongo thing. Eg,...

I had an object that, viewed with my general purpose display program, looks like this:

Doesn't look like it should be a problem. Addressing it as...

var item=recipient[0][0];

recipient[0] gave me the first array element, an object.

recipient[0][0] attempted to to address the object containing the email address and came back undefined.

Nothing I could do gave me the email address. It was insane.

Turns out that I could google the answer pretty easily once I realized what was going on but only after tearing my hair out because no one mentioned that mongoose produces bizarre data structures.

The answer is (drum roll, please)...

.lean()

as in...

query.lean().exec(wrapCallback(callback)); //this produces a plain, Javascript object

as compared to

query.exec(wrapCallback(callback)); //this produces a mongoose object from hell

Obviously, if you are doing your processing in a model, you might really like the benefits of using the mongoose data type. I bet there are a TON of them but, if you are trying to construct a test, as I was, or send it to a web page, like I plan to do, you are going to want something that acts like a civilized citizen, not some rogue toxic waste factory.

But, I wouldn't have minded at all if somewhere in the mongoose quick start and documentation they had said, "The output of find() must be accessed with mongoose utilities unless you add the lean() function to convert it to plain Javascript."

Maybe you got to this note before you actually ruined yourself. Or, maybe you, like everyone else on earth, already know.


Mongo Mongoose Simple Tutorial Quick Start Code Example

How's that for a search engine friendly title? It's a combination of the things I tried as I started my experience with Mongo/mongoose that did not give me what I wanted. What I did get were all sorts of useful explanations and advice. What I did not get is a simple bit of working code to satisfy my Hello World needs.

The code below includes the lesson, 'use createConnection(), not connect()'. The former allows you to access the database from separate model classes without having to pass a connection object around. In the depths of the mongoose forums, the fancy guys admit that they prefer createConnection() so it's ok.

It also has my curried default test callback. I had a problem when I tested the example that required it so I left it in so you can solve whatever screws it up for you more easily.

I had two problems you might want to know about:

You will notice that the boilerplate has a peculiar model name (userTqTest). I built this example inside my real, working project. It turns out that Mongo does not forget old model definition stuff. I haven't quite worked out the details but the bottom line is that when I declared this model as User and saved, I got errors based on the validation in my previous, real model. That is, the mongoose model does not supersede the one inside Mongo itself. I got over it by, first, dropping the collection. Then, since I don't want you to have to drop your User collection, I changed it to a name that is very unlikely to collide with you database.

Second is one that you probably will notice instantly but, because I was focused so much on showing the simple syntax, I didn't think about right away is that these calls to save, get and delete are asynchronous. They are triggered in the right order but they are happening in whatever order Mongo takes care of it.

I didn't want to complicate the example with async or some other method to sequence the calls, so I didn't. This code definitely works if you run the three functions one at a time but give unpredictable results otherwise. My problem was that the delete fired before the save was complete leaving the record to give me a duplicate key error on the second pass.

Enjoy.

========================================

        var mongoConnection = mongoose.createConnection("mongodb://localhost:27017/test");
        mongoConnection.on('error', function(err) {
            throw ("user code says, mongoose failed")
        });

        var userSchema = new Schema({
            userName: {
                type: String,
                unique: true
            } /*obviously you'd have more properties*/
        });

        var userAccessor = mongoConnection.model('UserTqTest', userSchema);

        var saveUser = function(user, callback) {
            /*you'd probably do some validation even though the mongoose will also validate
            you'd also wrap stuff in try/catch but that's not the point of this example.*/

            var newUser = new userAccessor(user);
            newUser.save(callback('saveUser' + '/' + user.userName));
        }

        var getUser = function(user, callback) {
            var query = userAccessor.findOne(user);
            query.exec(callback('getUser'));

        }

        var deleteUser = function(user, callback) {
            userAccessor.remove(user, callback('deleteUser'));
        }

        var callback = function(callingName) {
            return function(err, result) {
                console.log(callingName + '============');
                console.dir(err);
                console.dir(result);
                console.log('=====');
            }
        }

        saveUser({
            userName: 'tqwhite'
        }, callback);


        getUser({
            userName: 'tqwhite'
        }, callback);


        deleteUser({
            userName: 'tqwhite'
        }, callback);




   


Running Multiple Versions of NodeJS on One Server

In the modern age, node installation is handled by the 'n', eg,

sudo n stable #the current stable version

or,

sudo n 0.10.40 # to install v0.10.40

Once you have installed a version, it never goes away. You can switch back and forth between versions instantly. That means that you can easily alternate between two (or more) versions if you want.

But, you might (as I did) have an app that must run on an old version but you otherwise want to use a modern version. That is, you want two different versions active at once.

This can be done by referring to the node binary with a fully qualified path. (Remember, when you type "node", your shell is simply giving you /usr/local/bin/node.)

The program 'n' allows you to find the path to the binary (assuming that you have previously installed it), ie,

n bin 0.10.40

->/usr/local/n/versions/node/0.10.40/bin/node

You can then type

/usr/local/n/versions/node/0.10.40/bin/node SOME_OLD_APP.js

into your shell or put it into your initialization script or whatever.

I create separate users for each of the apps I have running on my server. There is only one that requires the old version of node. I want to use the latest stable version for everything else so, I have done "n stable". For the user account where I run the old app, I change my .bash_alias file to include...

alias node="/usr/local/n/versions/node/0.10.40/bin/node"

Then I can type

node SOME_OLD_APP.js

and get good results, or,

node --version

->v0.10.40

But when I log in as the user for a different app, I get the latest stable version.

Obviously, you can do this for as many node versions as you want but, if you need a lot, you might want to think about why (for me, it is Meteor, which only runs on old node).


UPDATE:

I have had occasion since I wrote this to install a new server. I was reminded that the utility 'n' does not come automatically with a NodeJS install. After you install NodeJS (which also installs npm), type

sudo npm install n -g

and the you can verify it by typing

n --help

and you will get all kinds of good info.

UPDATE 1/30/19

I was just reading about nvm, an alternative to n. It does its job by manipulating a special variable, not sure what. It doesn't move tings around. Maybe that would be good.