XAMPP phpMyAdmin subdomain on localhost

Create a localhost subdomain for phpMyAdmin on XAMPP

  • Open xampp\apache\conf\extra\http-vhosts.conf and append the following (modify the DocumentRoot for your XAMPP installation path):


## Added to serve as primary recipient of web requests:
<VirtualHost *:80>
    DocumentRoot "C:/Users/XYZ/xampp/htdocs"
    ServerName localhost
</VirtualHost>


## Added to get phpMyAdmin onto a subdomain:
<VirtualHost *:80>
    DocumentRoot "C:/Users/XYZ/xampp/phpMyAdmin/"
    ServerName phpmyadmin.localhost
</VirtualHost>






  •  Open C:\Windows\System32\drivers\etc\hosts and append the following:
127.0.0.1       phpmyadmin.localhost





  • Restart Apache


Hack to download files from an S3 bucket when browser cache won't restart.

An issue today was that eFinalDate was accessing the S3 bucket, and the images were being cached, so they wouldn't download the new settings on the bucket. This was on a customer's computer.  Fortunately, he was great to work with. Patient, and understanding

I was trying to call the url from angular using $http.get(s3-amazon-bucket-url).

We tried the following
  • Clear the cache. It worked, but once the image was recached, it wouldn't work.
  • Clear the cache. For whatever reason, the customer's cache wouldn't clear.
  • Go to developer settings, click on Network, and check Disable cache. This worked while the developer window was open, but not once it was closed.
  • Tried Ctrl-F5 to do a hard refresh
None of these worked. Sometimes the change would be temporary, other times not.

Finally, we tried appending a random string on the end of the get url. so it looked like : https://s3-us-west-1.amazonaws.com/efinaldate-images/prod/orders/1/1/eFinalDate-230-sized-21e6088c.jpg?2093930290393 (tied to miliseconds).

This actually worked. 

We did a big no-no, and pushed this one line change on the server in the middle of the day 😁
var url = order.processed_url + '?' + Date.now();

And, well, it worked. This is a total hack, and recognizes that the cache is based on the url, but the file is only based on the first part of the url. The url is now different, so it asks the server for the latest version of the file.

Lessons Learned #2

Aug 10, 2017

New project, should I use ES 6 or ES 5.1?

So, despite all the red showing that ES 6 isn't supported by older browsers 
Like here: or here or here. I decided to go with ES6 for a new project. 

I mean, ES5 was standardized in 2009. 8 years ago. According to Moore's law, that's like 6 generations, or roughly the equivalent of 120 human years. Best not to start with something that ancient!  I'm going with ES 6. 

Sigh ... well, one drawback is that my muscle memory types 'var' when now I guess I should be using 'let' ...😆

More promises and better ways

I've got a lot to learn. But I found this, it was clear and easy to understand. Kudos to the folks at Tao of Code

Requiring and importing code ... yeah parentheses matter!


I started with this
database.js
module.exports.database = function () {
    return mysql.createPool(config.database);};

And used it like this
let Database = require ('./database').database();

But I don't like the .database(); on the end.
To fix that, I changed it to this

database.js
module.exports = function () {
    return mysql.createPool(config.database);}();

And use it like this.
let Database = require ('./database');

Little things, but those parentheses really matter. I had to make sure to export the results of the executed function, not the function...

Lessons Learned #1

Aug 7, 2017

So, the purpose of this series of posts, is to just talk about the lessons learned each day.  We discover and uncover all kinds of things that either make our work easier, or have been making our life difficult.  The goal here is to codify our experience so that we can at least remember what we learned, and perhaps help our fellow man if they come across the same problem.

Of exports =  vs. module.exports = ???

Basically, exports is a reference to module.exports.  Meaning, if you change module.exports, then exports is changed too. In contrast if you change exports to something else, then module.exports and exports are now pointing at two separate things.

I choose to use module.exports =
Just so I don't have to remember that those two are different.

Source


Of Promises and Promise chains not being run sequentially

So, this one threw me for a loop for longer than I care to admit.

I was testing promises and this did not work, the getAlls were being run out of sequence 

print()
    .then(contact.truncate())
    .then(contact.getAll())
    .then(contact.getAll())
    .then(print())
    .then(print())
    .then(print())
    .then(function(){
        done();
    });

The problem was that I had the '()' on the end of the function. When I removed it, from each line, sure enough, it worked. The reason for this (I think) is that above, contact.getAll() would be the actual promise, not the function that returns a promise, whereas below, contact.getAll is the function that will return a promise.


print()
    .then(contact.truncate)
    .then(contact.getAll)
    .then(contact.getAll)
    .then(print)
    .then(print)
    .then(print)
    .then(function(){
        done();
    });

Anyway, it took too long to figure out, hopefully it helps someone.

More promises and parameters

This worked
return new Promise (function (fulfill, reject) {
    runQuery(query, values).then(function (results1) {
        self.getOne(obj.id).then(function(results) {
            fulfill(results);
        });
    })
})

This did not work
return new Promise (function (fulfill, reject) {
    runQuery(query, values)
       .then(self.getOne(obj.id))
       .then(function(results) {
            fulfill(results);
        });
    })
})

In the first case, I got what I wanted, which was the result of self.getOne.  In the second case, fulfill(results) sent the results from runQuery.  I didn't expect that...It is because I passed in self.getOne(obj.id) with parameters, which, processes the function before sending it.  I'll get it right eventually!

Bodyparser settings?

I had this error

body-parser deprecated bodyParser: use individual json/urlencoded middlewares node_modules/express/lib/router/layer.js:95:5

express deprecated req.host: Use req.hostname instead node_modules/body-parser/index.js:100:29
body-parser deprecated undefined extended: provide extended option node_modules/body-parser/index.js:105:29

when my code looked like:

var bodyParser = require('body-parser');
app.use(bodyParser);

I needed to switch to look like:
var bodyParser = require('body-parser');
app.use(bodyParser.urlencoded({extended:true}));
app.use(bodyParser.json);

Then everything worked fine.

Angular pretty URL 

So, default angular.js (1.5.3) has a # in the url. You can mitigate that pretty easy to make pretty urls by adding <base href="/"> inside the <head> element and after the <meta> tags. Also, in the angular_routes.js file add the '$locationProvider.html5Mode(true);'
angular.module('myapp')
    .config (['$routeProvider', '$locationProvider',
    function ($routeProvider, $locationProvider) {
        $routeProvider
            .when('/home', {
                template: ''
            })
        $locationProvider.html5Mode(true);
     }
]);

Node.js api routes not including the body?

I found that the reason my body parameters was empty was because I had called the appropriate middleware functions in the wrong order in the node.js express server.

For example, this left the body empty
var express = require('express');
var app = express();
var routesApi = require('./routes/routes');
var bodyParser = require('body-parser');

app.use('/api', routesApi);
app.use(bodyParser.urlencoded({ extended: true }));
app.use(bodyParser.json());

And this worked
var express = require('express');
var app = express();
var routesApi = require('./routes/routes');
var bodyParser = require('body-parser');

app.use(bodyParser.urlencoded({ extended: true }));
app.use(bodyParser.json());
app.use('/api', routesApi);

Sources







Check your build scripts

Might be common sense to many out there, but this got me today. A few weeks, month ago, I updated Mac OS X from El Capitan to Sierra. Its been fine.

I've been debugging an issue that doesn't make sense, a portion of code that has been working, stopped today, and I'm trying to figure out why. In exploration, I ran a build script that I haven't changed in forever, and I got this error


Solving it came from this fellow here: https://stackoverflow.com/questions/26185978/macos-wchar-h-file-not-found. Specifically, that xcode had decided to install command line libraries in another directory. Sigh...

Well a little of

  
xcode-select --install

then
  
sudo xcode-select --switch /Library/Developer/CommandLineTools/
And voila! It compiled again. So, yeah, should be policy to run build script right after upgrading OS.

Listen to your customers


So, as you know, one of our services is eFinalDate.com. This is one of those crazy little ideas that just seems to make sense, because it takes an old manual way of doing things, and puts it into a mobile phone. And all those ideas make sense right? 

Well, we interviewed one of our clients a few days ago, and learned a lot about the benefits she sees. Some of the things I didn't even know they used in their process and somethings I never would have thought of as a possibility for having value, or at least, why would anyone care or notice that.

Here are some of the benefits of eFinalDate.com 

Benefits of using eFinalDate

For the person who used to do rubbings

  • Faster to take measurements of monument with the camera
  • No messy carbon paper and rubbing material
  • Immediate and traceable delivery of monument measurements to the monument maker

For the designer
  • More joy doing final date orders.
  • Faster to import measurements into design software
  • Faster to match fonts with high confidence
  • Faster to match line spacing between rows of text
  • Faster to match spacing between characters in a row of text
  • Confidence that stencil will not overlap unseen figures on monument

For the company
  • Faster for designers to finish final date and other design orders
  • Faster to know the status of the order
  • Faster to receive orders and measurements from customers
  • Designers enjoy final date orders more
  • Better customer experience and safety net by providing the customer with an approval picture showing what the results will look like on the actual monument prior to performing the work.
  • Less printing paper and toner
  • Clear communication between designer and setter about where the stencil should be positioned on the monument.

Lesson: Talk to the customers

Prior to this experience, I thought it was all about the taking of the picture, and a little benefit from the designer's perspective. I never knew how much paper and toner they used to prove out the new design from a rubbing. Or how many times a designer might have to get up and walk to the printer, then back to the seat, sit down, and try again.

See, the old way of copying information is to have someone go to a cemetery and create a rubbing of the data on the tombstone. That rubbing becomes the measurements and is shipped back to the shop for the designer to work on. But they have to wait for it.

Once it arrives, and they are ready, they grab a ruler and measure the rubbing. And create text in their design software that matches the text on the rubbing with the suspected size. Then they print the text (careful to keep it small enough so the printing doesn't auto-scale to something inaccurate). Then the fold the paper so a crease is right through the letter they want to measure. It might be off by 1/8" so, the designer makes an adjustment in the design software, hits print, hits okay, walks to the printer (waits for it to print), comes back, sits down, measures again. If it looks good, she tries a few other letters until she is satisfied. If it doesn't look good, then keep printing and trying again.

Now this is all for one line of text. But a monument usually has 3 or 4: lines of text Title, name, birth date, death date. *Sigh* 😩

So the same process must be done for each line of text. Now we are at about 6 printed pages per line * 4 lines = 24 pages.

But now we have to handle the spacing of the lines. Half a dozen more pages, and one stencil. It only cost us 0.288% trees for the paper and maybe $0.06 * 30 = $1.80 of toner, and 10 health motivating steps * 30 pages = 300 steps. But hey at least the fit bit is happy!

Learning the benefits from the customers perspective. Helped us see better ways to describe the benefits to prospective customers.


Profit-sharing

Profit-sharing is a fun topic, but I want to take a look at a different meaning of the phrase.  Rather than look at the process of distributing a portion of a company's profit to involved parties, I want to look at the idea of publicly, transparently sharing internal company data on profit, revenue, and expenses.

Of course publicly traded companies do so, but there are a different set of considerations for a small, private company.

For starters, there's a generally-accepted rule that you don't share what you don't have to.  Keep your cards close to your chest.  Why hang yourself?  As Aaron Burr says in the hit Broadway show "Hamilton":



I learned this lesson myself at my previous job.  When a rift developed between two factions of management, there were a lot of words expended in accusations, arguments, and attempts to convert employees to their side.  What I quickly discovered was that it didn't matter what someone said--it would be interpreted a predetermined way by their opponent.

Side A: "I love puppies."
Side B: "Did you hear that?!  Side A hates kittens!"

What isn't said, can't be misinterpreted.  Often, the safest course is often to say nothing at all.



However, as both Aaron Burr and Johnny Tightlips discovered, there are repercussions to being opaque, usually involving someone getting shot.  Fortunately we avoided that outcome at my previous job, but it did cause my partner and I to have a deeper conversation about whether we wanted to share our company financials with the broader world.

Would we get robbed?  Would our company be denigrated?  Would a customer or partner refuse to do business with us because our numbers revealed that we were "smaller" than they were comfortable with?  Would our finances or lack thereof give leverage to the opposition in a negotiation scenario?  Would our money-laundering operation be exposed?

When you're a small company, you're already living closed to the edge and anything can tip you over. You don't make many decisions lightly.  In the end, however, we decided to publicly share our revenue because quite frankly, it was data that we would find valuable if provided by someone else.  Honest, live numbers from startups are hard to come by, particularly an unsplashy startup like our own that didn't start with $100M in venture capital and an established PR machine.  We're doing this the hard way: living on cold stewed beets, hunting a niche to work in, slogging through development, painstakingly building a customer base.

Our hope is that our numbers provide context, and maybe even hope for someone else.  "Oh, not every startup strikes it rich or wins the lottery?  Good, then I don't feel so bad.  If they did it, I can keep doing it too."

And if nothing else, maybe our numbers simply provide a little amusement!

XAMPP phpMyAdmin subdomain on localhost

Create a localhost subdomain for phpMyAdmin on XAMPP Open xampp\apache\conf\extra\http-vhosts.conf and append the following (modify the Docu...