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:
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.
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...
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.
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.
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);'
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);
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
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 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!