Thursday 26 May 2022

How I Became a Married Software Developer (Part 3/3)

When most people think of marriage, they think of sunset strolls along the beach, declarations of everlasting devotion, lots of passionate sex and a whole lot of crap they probably derived from reading too many shitty romance novels. Children, you are going to be so disappointed when you finally grow up. Let His Teochewness educate you - marriage is about sharing.

For the first time in five years, I had to share wardrobe space, the bed, and the bathroom. I had to get used to stocking every bathroom product known to man. I had to get used to having her facial masks taking up space in my refrigerator (or rather, our refrigerator). It was a lot of logistics. I had to write my CPF allocation and my Will - things I didn't have to think about when I was a bachelor.

Yep, now I have to
deal with this shit.

The Missus took over the apartment as soon as she got the keys. In between criticizing the standard of my housekeeping (and I took great offence to that, by the way), rearranging the furniture and throwing out a full three-quarters of my wardrobe, she also found time to impose some house rules. From now on, no more vagabond, I-don't-give-a-shit look. Even if I left the house for five minutes, I was expected to comb my hair. In fact, I was supposed to bring a comb everywhere with me. She got me new clothes - proper pants (with no holes in them), form-fitting t-shirts because she liked my figure, jackets. I'd become her personal Ken doll.

With her, making oneself presentable was about respecting the people around. I personally saw no such need... but I was married now and it wasn't about me anymore. Incidentally, the approval from my mother and sister was palpable.

Other benefits

Other than the fact that she's totally co-opted laundry duties and I no longer need to concern myself over them? Well, let's see...

You know the situations where you keep having to find a nice way to say "no"? Turns out, "I need to discuss this with my wife." is one heck of a great line.

"Hey, can we crash at your place this weekend?" "I need to discuss this with my wife."

"Dude, can you lend me some money?" "I need to discuss this with my wife."

"Have you considered taking up yet another life insurance policy?" "I need to discuss this with my wife."

Ask her.

Absolutely brilliant. For all of you reading this, know that I am going to abuse this line to death, just because I can. And if you're a little slow on the uptake and somehow haven't cottoned on yet, it means no.

Also, having that wedding ring on my finger lends me that air of respectability. Employers and colleagues (and prospective employers) are going to suddenly take me more seriously because that wedding ring means I'm a responsible adult, never mind the fact that I've been doing this job, married or otherwise, professionally for the past decade. It is what it is; people are going to be stupid about things like this, and I may as well take advantage of it.

Why?

Why indeed? Why give up all the freedoms I enjoy in my bachelorhood?

For those who are going to act all self-satisfied and say condescending shit like "it was time you grew up" and "every man needs a woman", you couldn't be more wrong. I'm still of the opinion that getting married just to adhere to some retarded backward KPI that people need to be married by a certain age, is an absolutely stupid reason to get married, and if that really was the only reason you got married... well, damn, dude.

No, I didn't suddenly lose half my brain cells and marry this woman just for the sake of being married. I got married because I wanted to marry this woman.

I choose this woman.

Gorgeous women are a dime a dozen. But I could live two lifetimes and still not encounter someone who could stick with me two entire years while I tried to get my shit together and stop being broke. I'm not going to be so crass as to call it "true love", but what I had was definitely something very few men could claim to have.

Case closed, and go fuck yourselves.

...for better for worse, for richer for poorer, in sickness and in health...

It's only my fourth year of marriage, and I'm learning as I go along. It could all still go south from here. It's certainly not been all sunshine and roses. I'm not just a web developer now; she turned me into a husband. It's a new role like any other. Seven years ago, I may not have planned for this, but so help me God, I am going to make an honest effort to earn it.

I do,
T___T

Monday 23 May 2022

How I Became a Married Software Developer (Part 2/3)

Halfway through my contract with the agency, I started toying with the idea of - dare I say it? - making it official. Marrying her. Putting a damn ring on it.

It had been out of the question when I was jobless, but at this point I was earning more money than I knew what to do with. Shit, I could probably feed her if she chose to quit her job and become a housewife. And, strangely, I had this disconcerting urge to take care of that woman... something I never felt with any of my ex-girlfriends. Which probably illustrates just how crappy a boyfriend I was in my youth.

Also, one day she took a day off and asked me to take her somewhere nice... because it was her birthday. And over a dinner of crabs and oysters, I realized with a start that she had just turned thirty-six. And it was then I thought to myself; time to shit or get off the pot. She wasn't getting any younger.

How I proposed

So I thought about it quietly, and stewed over it some more for a month. Examined my options. Even drafted a nice speech that could be used as a proposal - points that made sense to me (and would make sense to her, I hoped) and sounded persuasive.

Unfortunately, I never got to use it.

Oh, shit.

Like many women, she had this knack of asking really awkward questions. And one such question came when she asked, do you love me or do you just like me?

Ouch.

There was no way a question like that could be safely navigated. Reply to the former, and it would sound cheesy and insincere. Reply to the latter, and it would raise the inevitable question of why we were even together in the first place.

So like the genius I am, I got defensive and babbled. There's no way I'm saying I love you. Words are cheap, and it wouldn't mean anything. But here, you can have my last name.

Her reaction surprised me. From aggressive, she turned uncharacteristically shy, nudged my shoulder with her head and purred something demure.

And seconds later, the enormity of what I'd just done, hit me with the force of a fucking almighty thunderbolt.

The plan

Well, too late now. The words were out and there was no taking them back, not that I was considering it. The only thing left was to plan how it was going to go down. You see, the thing about me is that I'm slow to action... but once I commit to a course, I am unshakeable.

The first course of action was to plan the date. This one was a delicate matter. A month after we got married, she would be eligible to apply for a Long-term Social Visit Pass (LSVP) which would allow her to stay in Singapore. However, the authorities would examine both her and my financial situation. The thing was that my salary was more than adequate - but I was on an employment contract which would expire in July. And if I had not secured employment by then, my income would be declared zero, which would then equal our chances of obtaining that LSVP. If we took processing time for that application into account, we would need to get married more than a month before my contract was up.

Thus, the Registry of Marriages appointment was set on the 28th of May - coincidentally one day after my final exam for ACTA! Way to pile on the pressure, eh?

No pressure, bro.

The second was informing my family. This was a delicate situation. My parents are suspicious of the mainland Chinese, disapproving of divorcees (being Catholic and all), are uncomfortable with marriages involving women who have children from previous marriages and not exactly forthcoming with non-Catholics. My bride-to-be checked all the boxes. And, being Asian parents, they think they can make better decisions than their kids and aren't shy about saying so. There was no way I was getting a vote of confidence.

So I didn't put it to the vote.

This wasn't a democracy. This was my marriage, a decision that would affect my life long after they were gone. I merely booked a table in the restaurant for after the Solemnization and invited them to attend. My parents insisted on a meeting with the bride beforehand, which I felt was reasonable. They didn't love her when they met her, but as far as I was concerned, I had fulfilled my obligations.

The third was securing a new job in the event that the LSVP process took longer than expected and they wanted to check how much I was making or if I was even employed. For that, I went on one of my annual interviewing sprees. It never came to that. The company not only renewed my contract, but also raised my pay by another ten percent. The LSVP part was good to go.

How it proceeded

First was the Registry of Marriages. We got through it fairly smoothly, though the occasion was somewhat marred by the fact that Liverpool had lost 3-1 to Real Madrid in the UEFA Champions League Final that morning, and I was still sore about it. In fact, I was probably the most despondent bridegroom in the building.

I do.

Three days later, we solemnized our marriage at the common corridor of my apartment, with my ex-boss and a couple of my NS buddies as witnesses. There was no grand reception, no hired photographer, no fancy shit - just mobile phones snapping pictures and bottles of peppermint ice tea. After which, the witnesses were invited to dinner with my family at the restaurant I had made the booking at.

It was the bride's idea. She wanted things kept simple, and frankly, thinking of having to go through tuxedo fittings, guest lists and restaurant banquet arrangements made me feel tired right away. The soon-to-be wife wanted things cheap and fuss-free, and if I'm going to be brutally honest, that suited me to a tee.

Next

How being married has felt like the past few years.

Friday 20 May 2022

How I Became a Married Software Developer (Part 1/3)

His Teochewness is a married man as of 2018.

By now, most of my friends (and readers of this blog) would have received the news, though if it comes as a surprise to those reading this, it's probably because I didn't update my marital status on Facebook or post pictures on Instagram the moment it happened. I mean, what's the point? So I can let the world know that somebody wants me? Lame.

Reactions were fun to watch, though. My colleagues were left scratching their heads at my business-as-usual attitude towards it (please, people get married in Singapore every damn day) and any friends I told the news to, had to pick their jaws off the floor.

Me: Oh, by the way, I'm married.
Them: Yeah yeah we know, you're married to your job.
Me: Uh, no... I'm married to a woman. (cue dumbstruck looks)


Part of their surprise was due to the fact that I've always been a natural-born bachelor. I don't actively shun female company or seek it, nor am I the type to get lonely. Much of my free time is spent cooped up in my apartment writing code and generally doing very boring (not to me, though!) geeky stuff. They didn't even know I was dating anyone... again, because I just don't advertise this shit on Social Media. Whatever for, right?

So anyway, my fourth wedding anniversary is coming up and I thought, what the hell, I should write this down for posterity. Because the long arduous road to marriage was quite intimately tied to the winding rocky road of my tech career.

Before I begin, I should warn you that this is not some grand love story (though probably still a better love story than Twilight) and you may be bored shitless.

How it began

Back in the year 2015, I had just been laid off and I was spending my days in the neighborhood coffee shop, reading emails and scrolling through LinkedIn ads. The crowd was mostly familiar friendly faces - retired old geezers, grandmothers, working men and women, and the like. One of them was this girl from mainland China who worked around the area and had lunch at this coffee shop daily. Occasionally, she'd join the table I was at, and we'd talk until she had to get back to work. She was pleasant enough, but not really on my radar at the time.

Coffee and job search.

Soon, I got a job at a startup and stopped spending my days at the coffee shop. I would still see her around on weekends.

Life at the startup kept me busy until the startup tanked in 2016 and I found myself out of a job and spending my days at the coffee shop once again. Yep, twice in the space of a year. Life sure wasn't looking good for me, eh?

The First Date

The woman and I started talking again, and this time, she was complaining about having nothing to do because the shop she was working in was closed during Chinese New Year, and getting a plane ticket to fly home for the holiday was just too much hassle. I waggled my eyebrows, put on my sleaziest expression and suggested that maybe the two of us could go somewhere romantic.

Please note that at that time, I was joking. I was freshly out of a job for the second time within twelve months, and chasing skirts was absolutely the last thing on my mind. I was fully prepared for her to laugh it off. So imagine my befuddlement when she said "OK" without missing a beat and asked me where I was going to take her. Now I was in a spot. If I'd confessed I was only fucking around, that would have been embarrassing for both of us.

So we made a date for East Coast Park. On the day itself, she had gone to visit her cousin and I was supposed to pick her up and take her there. The sky was overcast, and it had drizzled in the morning.

Beach stroll.

We got to East Coast Park, took a nice long stroll along the windy beach hand-in-hand like some sweet young couple, had a late supper of handmade noodles, after which I sent her home.

And that was it. A perfectly run-of-the-mill first date.

Afterwards

As the days after that date went by, we started hanging out. I was still jobless, but we took nice midnight strolls around the gardens peppering the neighborhood. We might even have snogged a couple times. She kept me going through this phase, and eventually, I landed a job, where I was paid more money than I'd ever been. And once I got busy with work, our midnight strolls decreased in frequency.

Months after starting that job, I began my course in ACTA, and soon had even less time. I don't remember if I ever mentioned this, but I've dated several women before; and each time I was a crappy boyfriend, obsessed with my work, obsessed with learning more. It looked like old habits were resurfacing, but unlike the others, she actually seemed to like it.

And then she left Singapore. Her employer had forgotten to pay the levy for her employment permit, and promptly, it was cancelled. She had to return to China within a month. After she left, I got on with the task of earning my ACTA while holding down my job. We kept in touch regularly via WeChat, and I realized, to my chagrin, God damn it, that I kind of missed her. I missed the roundness of her face, the smallness of her hands, the way she'd spit the bones of her food out on the table, or pick her teeth with her fingers in front of me.

I know, I know, it's objectively gross. Don't judge me.

Use a toothpick, woman.

When she returned, a couple months later, it was by securing another job a lot further away from my place. This meant that we didn't see each other as often as we used to, though we still hung out on weekends.

Though at this point, I'm not sure why she still bothered. She had often pondered getting married here in Singapore because it would make working here a lot less troublesome, while I was quite adamant that I'd remain a bachelor till the end of my days. Up to this point, I hadn't even thought of her as my girlfriend, though in hindsight, for all intents and purposes, she was pretty much that.

Next

How we ended up getting married.

Sunday 15 May 2022

Web Tutorial: Florida Man Headline Generator

The Florida Man meme is an internet phenomenon due to the frankly bizarre headlines that come up every time someone does an internet search for the term "florida man". Honesely, I'm not so sure what's special about this swampy state in the USA, but it is what it is.

Just to get in some practice with VueJS, today we will be making a Florida Man headline generator. It won't be much - it will generate eight random headlines every time the page refreshes.

I plan to use images - but on the off-chance you don't want to, or want to keep images at a minimum, just consider using this one.

floridaman.jpg


Let's get cracking!

The starting HTML is here. Notice that we have a remote link to Vue.
<!DOCTYPE html>
<html>
    <head>
        <title>Florida Man</title>

        <style>

        </style>
    </head>

    <body>
        <script src="https://cdnjs.cloudflare.com/ajax/libs/vue/1.0.18/vue.min.js"></script>

        <script>

        </script>
    </body>
</html>


For this, in the HTML, we want a div in there with a class of floridamanApp. And in the JavaScript, we make a call to create a new Vue object.
<!DOCTYPE html>
<html>
    <head>
        <title>Florida Man</title>

        <style>

        </style>
    </head>

    <body>
        <div id="floridamanApp">

        </div>

 
        <script src="https://cdnjs.cloudflare.com/ajax/libs/vue/1.0.18/vue.min.js"></script>

        <script>
            var app = new Vue
            (
      
            );

        </script>
    </body>
</html>


Within that div, we have eight divs, each styled using the CSS class headlineContainer. Each one has an inner div styled using the CSS class headline. And in turn, each div has a h1 tag.
<div id="floridamanApp">
    <div class="headlineContainer">
        <div class="headline">
            <h1></h1>
        </div>
    </div>

    <div class="headlineContainer">
        <div class="headline">
            <h1></h1>
        </div>
    </div>

    <div class="headlineContainer">
        <div class="headline">
            <h1></h1>
        </div>
    </div>

    <div class="headlineContainer">
        <div class="headline">
            <h1></h1>
        </div>
    </div>
 
    <div class="headlineContainer">
        <div class="headline">
            <h1></h1>
        </div>
    </div>

    <div class="headlineContainer">
        <div class="headline">
            <h1></h1>
        </div>
    </div>

    <div class="headlineContainer">
        <div class="headline">
            <h1></h1>
        </div>
    </div>

    <div class="headlineContainer">
        <div class="headline">
            <h1></h1>
        </div>
    </div>

</div>


We will fill up the first div with some sample content within the h1 tag. The curly bracer pairs are placeholders.
<div class="headlineContainer">
    <div class="headline">
        <h1>Florida Man {{ }} {{ }} {{ }} {{ }} for {{ }} {{ }}!</h1>
    </div>
</div>


Now, admittedly, this isn't much. This needs some styling.




For the headlineContainer CSS class, we provide a specific width and height, set the float property to left and give it a top and left margin of 5 pixels. That's for layout.
<style>
    .headlineContainer
    {
        width: 500px;
        height: 200px;
        float: left;
        margin: 5px 0 0 5px;
    }

</style>


We then give it round corners and a thick grey border, make sure the background-size property is set to cover (because there will be a background image later on) and ensure that it's set to a vintage coloring by applying grayscale at 100%. Lastly, we set the overflow property to hidden in order to ensure that the HTML contents are neatly handled.
<style>
    .headlineContainer
    {
        width: 500px;
        height: 200px;
        float: left;
        border-radius: 10px;
        border: 5px solid rgba(100, 100, 100, 1);

        margin: 5px 0 0 5px;
        filter: grayscale(100%);
        background-size: cover;
        overflow: hidden;

    }
</style>


For the headline CSS class, we set width and height at 100%, float it left and apply a translucent black background.
<style>
    .headlineContainer
    {
        width: 500px;
        height: 200px;
        float: left;
        border-radius: 10px;
        border: 5px solid rgba(100, 100, 100, 1);
        margin: 5px 0 0 5px;
        filter: grayscale(100%);
        background-size: cover;
        overflow: hidden;
    }

    .headline
    {
        width: 100%;
        height: 100%;
        float: left;
        background-color: rgba(0, 0, 0, 0.5);
    }

</style>


This looks OK-ish. For headlineContainer, the overflow property has been set to hidden, so you won't see any ugly seams at the edges. The layout appears to be working nicely.




For the h1 tag, we add some padding and specify the width. Center everything. Make the words captialized.
.headline
{
    width: 100%;
    height: 100%;
    float: left;
    background-color: rgba(0, 0, 0, 0.5);
}

.headline h1
{
    text-transform: capitalize;
    text-align: center;
    padding: 10px;
    width: 480px;
}


Then we make the text all white and use the text-shadow property to give it a black outline.
.headline h1
{
    text-transform: capitalize;
    text-align: center;
    color: rgba(255, 255, 255, 1);
    text-shadow: 1px -1px 0 rgba(0, 0, 0, 1), -1px 1px 0 rgba(0, 0, 0, 1), -1px -1px 0 rgba(0, 0, 0, 1), 1px 1px 0 rgba(0, 0, 0, 1);

    padding: 10px;
    width: 480px;
}


Yeah! Looks great!




That's out of the way. We are going to do some JavaScript next. For Vue, we have to specify data and methods. And before we forget, set the el property to interact with floridamanApp.
<script>
    var app = new Vue
    (
        {
            el: "#floridamanApp",
            data:
            {

            },
            methods:
            {
            
            }
        }

    );
</script>


For data, we have headlines and wordTypes, both arrays. We will deal with headlines in a bit, but let's first take a look at wordTypes. Each element is an array of words. You can fill this up with whatever words you wish. I've done a few right here.
data:
{
    headlines:
    [

    ],
    wordTypes:
    {    
        nouns:
        [
            "alligator",
            "businessman",
            "cop",
            "dragonfly",
            "ewe",
            "florist",
            "girl",
            "hamster",
            "iguana",
            "joker"
        ],
        nouns_plural:
        [
            "alligators",
            "businessmen",
            "cops",
            "dragonflies",
            "ewes",
            "florists",
            "girls",
            "hamsters",
            "iguanas",
            "jokers"
        ],
        numbers:
        [
            "three",
            "six",
            "thirteen",
            "nineteen",
            "twenty-three",
            "fifty-nine",
            "eighty-seven"
        ],
        units:
        [
            "bags",
            "crates",
            "pounds",
            "pairs",
            "consignments"
        ],
        units_time:
        [
            "days",
            "hours",
            "minutes",
            "weeks"
        ],
        actions:
        [
            "antangonize",
            "brutalize",
            "chase",
            "kill",
            "marry",
            "proposition",
            "question"
        ],
        actions_past:
        [
            "antangonized",
            "brutalized",
            "chased",
            "killed",
            "married",
            "propositioned",
            "questioned"
        ],
        actions_plural:
        [
            "antangonizes",
            "brutalizes",
            "chases",
            "kills",
            "marries",
            "propositions",
            "questions"
        ],
        actions_active:
        [
            "antangonizing",
            "brutalizing",
            "chasing",
            "killing",
            "marrying",
            "propositioning",
            "questioning"
        ],
        descriptors:
        [
            "anxious",
            "articulate",
            "blasphemous",
            "blind",
            "caustic",
            "cowardly",
            "dead",
            "dramatic",
            "enormous",
            "epic",
            "friendly",
            "furious",
            "generous",
            "gorgeous",
            "heavy",
            "huge",
            "impassive",
            "irate",
            "jovial",
            "jumpy",
            "kind",
            "knowledgeable",
            "lascivious",
            "livid",
            "monstrous",
            "mysterious"
        ]
    }

},


Now, for headlines. Each element in headlines is an object consisting of key-value pairs. These keys should have an equivalent to the keys in wordTypes, and the values are arrays of empty strings. Note that descriptors has two empty strings instead of one. You'll see why later on.
headlines:
[
    {
        actions_plural: [""],
        descriptors: ["", ""],
        nouns: [""],
        numbers: [""],
        units_time: [""]
    }

],


Let's create some methods! We begin by having a fillHeadlineArray() method. It has a parameter, headlineIndex. This is a pointer to the element in headlines that we want to change, or fill, as it were. There is only one element in headlines right now, but that is going to change later.
methods:
{
    fillHeadlineArrays: function(headlineIndex)
    {
                                                        
    }

}


We first get keys by taking all the keys of the wordTypes array. What we want to do is use a For-each loop to iterate through the keys.
methods:
{
    fillHeadlineArrays: function(headlineIndex)
    {
        var keys = Object.keys(this.wordTypes);

        keys.forEach(
            (wordType) =>
            {

            }
        );
                                                        
    }
}


And in it, search the current element of headlines for that particular key. If it exists...
methods:
{
    fillHeadlineArrays: function(headlineIndex)
    {
        var keys = Object.keys(this.wordTypes);

        keys.forEach(
            (wordType) =>
            {
                if (this.headlines[headlineIndex][wordType])
                {

                }

            }
        );                                                        
    }
}


...fill each empty string in that particular array, with a randomly selected element from that particular array in wordTypes. For this, we use a For loop and the getOne() method.
methods:
{
    fillHeadlineArrays: function(headlineIndex)
    {
        var keys = Object.keys(this.wordTypes);

        keys.forEach(
            (wordType) =>
            {
                if (this.headlines[headlineIndex][wordType])
                {
                    for (let i = 0; i < this.headlines[headlineIndex][wordType].length; i++)
                    {
                        var newWord = this.getOne(this.wordTypes[wordType]);

                        this.headlines[headlineIndex][wordType][i] = newWord;
                    }

                }
            }
        );                                                        
    }
}


This is the getOne() method. It accepts an array, arr. We just randomly pick one element from arr and return it.
methods:
{
    fillHeadlineArrays: function(headlineIndex)
    {
        var keys = Object.keys(this.wordTypes);

        keys.forEach(
            (wordType) =>
            {
                if (this.headlines[headlineIndex][wordType])
                {
                    for (let i = 0; i < this.headlines[headlineIndex][wordType].length; i++)
                    {
                        var newWord = this.getOne(this.wordTypes[wordType]);

                        this.headlines[headlineIndex][wordType][i] = newWord;
                    }
                }
            }
        );                                                        
    },
    getOne: function(arr)
    {
        return arr[Math.floor((Math.random() * arr.length))];
    }

}


Now in Vue, set created.
methods:
{
    fillHeadlineArrays: function(headlineIndex)
    {
        var keys = Object.keys(this.wordTypes);

        keys.forEach(
            (wordType) =>
            {
                if (this.headlines[headlineIndex][wordType])
                {
                    for (let i = 0; i < this.headlines[headlineIndex][wordType].length; i++)
                    {
                        var newWord = this.getOne(this.wordTypes[wordType]);

                        this.headlines[headlineIndex][wordType][i] = newWord;
                    }
                }
            }
        );                                                        
    },
    getOne: function(arr)
    {
        return arr[Math.floor((Math.random() * arr.length))];
    }
},
created: function()
{

}


Let's use a For loop to iterate through headlines, running the fillHeadlineArrays() method for each one. Remember, right now, and I know I risk sounding like a broken record at this point, there is only one element in headlines.
created: function()
{
    for (let i = 0; i < this.headlines.length; i++)
    {
        this.fillHeadlineArrays(i);
    }

}


In the HTML, we need to put in the references in the placeholder bracers. Note that "descriptors" appears twice. That's because the descriptors array in the first (and only) element of headlines has two elements. So the first one is referenced using index 0, and the second using index 1.
<div class="headlineContainer">
    <div class="headline">
        <h1>Florida Man {{ headlines[0]["actions_plural"][0] }} {{ headlines[0]["descriptors"][0] }} {{ headlines[0]["descriptors"][1] }} {{ headlines[0]["nouns"][0] }} for {{ headlines[0]["numbers"][0] }} {{ headlines[0]["units_time"][0] }}!</h1>
    </div>
</div>


Let's see what we get!




Let's make another headline. This time, the elements of headlines are indexed using 1, whereas in the previous one, they were indexed using 0.
<div class="headlineContainer">
    <div class="headline">
        <h1>Florida Man {{ headlines[0]["actions_plural"][0] }} {{ headlines[0]["descriptors"][0] }} {{ headlines[0]["descriptors"][1] }} {{ headlines[0]["nouns"][0] }} for {{ headlines[0]["numbers"][0] }} {{ headlines[0]["units_time"][0] }}!</h1>
    </div>
</div>

<div class="headlineContainer">
    <div class="headline">
        <h1>Florida Man {{ headlines[1]["actions_past"][0] }} by {{ headlines[1]["nouns"][0] }}  for {{ headlines[1]["actions_active"][0] }} {{ headlines[1]["nouns"][1] }}!</h1>
    </div>
</div>

<div class="headlineContainer">
    <div class="headline">
        <h1></h1>
    </div>
</div>


In headlines, we add another object. This one has a different set of word types.
headlines:
[
    {
        actions_plural: [""],
        descriptors: ["", ""],
        nouns: [""],
        numbers: [""],
        units_time: [""]
    },
    {
        actions_past: [""],
        nouns: ["", ""],
        actions_active: [""]
    }

];


Again, let's see what we get. Notice that "dragonfly" appears twice in the second headline. It's a little boring to have the same words being used, so let's take care of that.




In the fillHeadlineArrays() method, add this line. It's a While loop that ends only if the randomly selected word isn't already in that particular array. Do note that this will result in an infinite loop if you have more elements in that particular array than there are in the corresponding wordTypes array!
fillHeadlineArrays: function(headlineIndex)
{
    var keys = Object.keys(this.wordTypes);

    keys.forEach(
        (wordType) =>
        {
            if (this.headlines[headlineIndex][wordType])
            {
                for (let i = 0; i < this.headlines[headlineIndex][wordType].length; i++)
                {
                    var newWord = this.getOne(this.wordTypes[wordType]);

                    while (this.headlines[headlineIndex][wordType].indexOf(newWord) != -1)
                    {
                        newWord = this.getOne(this.wordTypes[wordType]);
                    }


                    this.headlines[headlineIndex][wordType][i] = newWord;
                }
            }
        }
    );                                                        
},


This should be fine now.




Adding images

What's a headline without an image? We will add a call to the getStyle() method in each div here, and pass in the current index of each div, as an argument.
<div class="headlineContainer" v-bind:style="getStyle(0)">
    <div class="headline">
        <h1>Florida Man {{ headlines[0]["actions_plural"][0] }} {{ headlines[0]["descriptors"][0] }} {{ headlines[0]["descriptors"][1] }} {{ headlines[0]["nouns"][0] }} for {{ headlines[0]["numbers"][0] }} {{ headlines[0]["units_time"][0] }}!</h1>
    </div>
</div>

<div class="headlineContainer" v-bind:style="getStyle(1)">
    <div class="headline">
        <h1>Florida Man {{ headlines[1]["actions_past"][0] }} by {{ headlines[1]["nouns"][0] }} for {{ headlines[1]["actions_active"][0] }} {{ headlines[1]["nouns"][1] }}!</h1>
    </div>
</div>


Create the getStyle() method. What we want is to grab images. Since right now we only use floridaman.jpg, we can put that in an array, use the getOne() method to pick it (it will pick this image because that's the only one) and return the style string!
getOne: function(arr)
{
    return arr[Math.floor((Math.random() * arr.length))];
},
getStyle: function(headlineIndex)
{
    var arr = ["floridaman"];

    if (arr.length == 0)
    {
        return "";
    }
    else
    {
        return "background: url(" + this.getOne(arr) + ".jpg)";
    }
}


There you go.




But what if you want more? Then we'll have to add more. Here, I'm creating the images array and populating it with the names of all the images sans extension. The images can be found in this repository.
getStyle: function(headlineIndex)
{
    var images = ["alligator", "businessman", "cop", "dragonfly", "ewe", "florist", "girl", "hamster", "iguana", "joker", "alligators", "cops", "girls", "hamsters"];

    var arr = ["floridaman"];

    if (arr.length == 0)
    {
        return "";
    }
    else
    {
        return "background: url(" + this.getOne(arr) + ".jpg)";
    }
}


Next, we define keys the same way we did in fillHeadlineArrays(). And then I do a For-each loop, going through the entire wordTypes array.
getStyle: function(headlineIndex)
{
    var keys = Object.keys(this.wordTypes);
    var images = ["alligator", "businessman", "cop", "dragonfly", "ewe", "florist", "girl", "hamster", "iguana", "joker", "alligators", "cops", "girls", "hamsters"];

    var arr = ["floridaman"];

    keys.forEach(
        (wordType) =>
        {

        }
    );


    if (arr.length == 0)
    {
        return "";
    }
    else
    {
        return "background: url(" + this.getOne(arr) + ".jpg)";
    }
}


We do a search for wordType in the current element of headlines. If it's found, we do an inner For-each loop to go through images.
keys.forEach(
    (wordType) =>
    {
        if (this.headlines[headlineIndex][wordType])
        {
            images.forEach(
                (img) =>
                {

                }
            )
        }

    }
);


... and then push img into the array arr if it is found in the list of words! Now the getOne() method should return more than one possible value if we pass in arr as an argument.
keys.forEach(
    (wordType) =>
    {
        if (this.headlines[headlineIndex][wordType])
        {
            images.forEach(
                (img) =>
                {
                    if (this.headlines[headlineIndex][wordType].indexOf(img) != -1) arr.push(img);
                }
            )
        }
    }
);


Great! In the first headline, the script chooses between floridaman.jpg and cop.jpg. In the second headline, the script chooses between floridaman.jpg, dragonfly.jpg and ewe.jpg.




We can add more content now in the HTML.
<div class="headlineContainer" v-bind:style="getStyle(0)">
    <div class="headline">
        <h1>Florida Man {{ headlines[0]["actions_plural"][0] }} {{ headlines[0]["descriptors"][0] }} {{ headlines[0]["descriptors"][1] }} {{ headlines[0]["nouns"][0] }} for {{ headlines[0]["numbers"][0] }} {{ headlines[0]["units_time"][0] }}!</h1>
    </div>
</div>

<div class="headlineContainer" v-bind:style="getStyle(1)">
    <div class="headline">
        <h1>Florida Man {{ headlines[1]["actions_past"][0] }} by {{ headlines[1]["nouns"][0] }} for {{ headlines[1]["actions_active"][0] }} {{ headlines[1]["nouns"][1] }}!</h1>
    </div>
</div>

<div class="headlineContainer" v-bind:style="getStyle(2)">
    <div class="headline">
        <h1>Florida Man set to {{ headlines[2]["actions"][0] }} {{ headlines[2]["descriptors"][0] }} {{ headlines[2]["descriptors"][1] }} {{ headlines[2]["nouns"][0] }} in {{ headlines[2]["numbers"][0] }} {{ headlines[2]["units_time"][0] }}.</h1>
    </div>
</div>

<div class="headlineContainer" v-bind:style="getStyle(3)">
    <div class="headline">
        <h1>Florida Man {{ headlines[3]["actions_plural"][0] }} {{ headlines[3]["numbers"][0] }} {{ headlines[3]["units"][0] }} of {{ headlines[3]["nouns_plural"][0] }} for {{ headlines[3]["descriptors"][0] }} {{ headlines[3]["descriptors"][1] }} {{ headlines[3]["nouns"][0] }}.</h1>
    </div>
</div>

<div class="headlineContainer" v-bind:style="getStyle(4)">
    <div class="headline">
        <h1>Florida Man is {{ headlines[4]["descriptors"][0] }} at {{ headlines[4]["descriptors"][1] }} {{ headlines[4]["descriptors"][2] }} situation involving {{ headlines[4]["descriptors"][3] }} {{ headlines[4]["nouns_plural"][0] }} and {{ headlines[4]["descriptors"][4] }} {{ headlines[4]["nouns_plural"][1] }}.</h1>
    </div>
</div>

<div class="headlineContainer" v-bind:style="getStyle(5)">
    <div class="headline">
        <h1>Florida Man unable to {{ headlines[5]["actions"][0] }} {{ headlines[5]["nouns"][0] }} due to {{ headlines[5]["descriptors"][0] }} {{ headlines[5]["nouns_plural"][0] }}.</h1>
    </div>
</div>

<div class="headlineContainer" v-bind:style="getStyle(6)">
    <div class="headline">
        <h1>Florida Man jailed for {{ headlines[6]["actions_active"][0] }} {{ headlines[6]["nouns_plural"][0] }} and {{ headlines[6]["actions_active"][1] }} {{ headlines[6]["descriptors"][0] }} {{ headlines[6]["nouns"][0] }}.</h1>
    </div>
</div>

<div class="headlineContainer" v-bind:style="getStyle(7)">
    <div class="headline">
        <h1>Florida Man wins {{ headlines[7]["descriptors"][0] }} contest against {{ headlines[7]["numbers"][0] }} {{ headlines[7]["units"][0] }} of {{ headlines[7]["descriptors"][1] }} {{ headlines[7]["nouns_plural"][0] }}.</h1>
    </div>
</div>


And in the headlines array.
headlines:
[
    {
        actions_plural: [""],
        descriptors: ["", ""],
        nouns: [""],
        numbers: [""],
        units_time: [""]
    },
    {
        actions_past: [""],
        nouns: ["", ""],
        actions_active: [""]
    },
    {
        actions: [""],
        descriptors: ["", ""],
        nouns: [""],
        numbers: [""],
        units_time: [""]
    },
    {
        actions_plural: [""],
        numbers: [""],
        units: [""],
        nouns_plural: [""],
        descriptors: ["", ""],
        nouns: [""]
    },
    {
        descriptors: ["", "", "", "", ""],
        nouns_plural: ["", ""]
    },
    {
        actions: [""],
        nouns: [""],
        descriptors: [""],
        nouns_plural: [""]
    },
    {
        actions_active: ["", ""],
        nouns: [""],
        descriptors: [""],
        nouns_plural: [""]
    },
    {
        numbers: [""],
        units: [""],
        nouns_plural: [""],
        descriptors: ["", ""]
    }

],


Our final product!




Thanks for reading!
This was totally awesome. And yes, really frivolous. Probably not really something you'd want to use VueJS for.

Florida Man writes boisterous web tutorial that confounds the livid majestic webosphere!
T___T

Wednesday 11 May 2022

Ten Pieces Of African Wisdom In Software Development

In May of 2021, I got onto the Clubhouse app and encountered many interesting communities. One of these were the Africans. I went into a room under the club African Father in America, where they were discussing African proverbs, and I was hooked ever since.

What can I say? Cultural exchange is my thing, yo.

For real, though, some of the African proverbs discussed could fit very well in a software development context. It is a professional hazard on my part; everything I hear almost always gets fitted in a software development context.

Today, I have put together ten of these, for your reading pleasure.

1. The best way to eat an elephant in your path is to cut him up into little pieces.

Divide and conquer. That is almost all of software development these days. Software projects are too large and complex to be handled at one go. They have to be broken down into multiple parts so that multiple developers can work on them concurrently.

Elephant for lunch, anyone?

Even if not for that, this is still the default way software developers are trained - to break up a problem into smaller parts so that each part can be completed in isolation from the rest. This makes it much easier to measure progress.

In software development, projects are the elephant and software developers are the diners.

2. He who does not know one thing knows another.

This has never been truer in the world of software development, where what used to be different skills in one large discipline, have each evolved into their own discipline. Front-end, back-end, database management, DevOps - all these require a certain expertise that certain people have dedicated their lives to.

It is true for almost certainly all developers, that expertise in one discipline requires an equal lack of expertise in others. That is right and natural, and nothing to be embarrassed about. After all, who can lay claim to all knowledge, past present and future, in the rapidly evolving arena of software development?

A union of various experts.

Therefore, developer teams are made up of people who each are a master of their own discipline, yet know enough about other disciplines to form a viable cohesive unit with one another.


3. The hunter in pursuit of the elephant does not stop to throw stones at birds.

This Ugandan proverb speaks to the power of focus, and is one of my personal favorites. Developers work best when they are not distracted, when they are given space to focus all their attention on any given task.

Get that elephant.

Any interruption, professional or otherwise, results in the developer losing his or her train of thought, and having to pick up where they left off after the interruption. And often, this is easier said than done.

When a developer is in the zone, they should not be disturbed if at all possible.


4. It is best to bind the finger before it is cut.

This saying comes from Lesotho, and it reminds me of all the preemptive efforts made by software developers - in the form of defensive programming - to forestall any mishaps that might occur in the code.

Preemptively
binding that finger.

Null values are checked for. Zero values as well, in case one has to perform a division operation. The upper and lower limits of a piece of functionality are defined. Assumptions are documented and later confirmed.

All this seems like a major pain in the ass. But it is a necessary pain in the ass. Because being caught out by elementary errors can be more costly than if they were nipped in the bud from the get-go. Every software developer knows this.

5. A person who has not secured a place on the floor should not look for a mat.

Nigeria gave birth to this proverb, and it is a reminder to software developers, especially recent graduates with little experience, to pay their dues.

If you want to sit here,
earn the right.

You cannot march into a project which you have little knowledge of, without understanding the intricacies of the interconnected parts or the problem statement, and assume you know better than those that have been working on it.

You cannot be freshly graduated from school and expect seasoned developers to give you the same deference they would give their peers. If you want to be treated as a peer, you have to be a peer. And that involves doing the work, staying humble, and learning as much as you can.

Hubris is often touted as a virtue of great programmers, but it can also be a fatal flaw.

6. You learn how to cut down trees by cutting them down.

As a software developer, I discovered the essence of this Bateke proverb the hard way. You learn by reading, by listening, and most by doing.

Mastery of cutting trees.

The willingness to pick up a book or watch a tutorial cannot be discounted. It is a step in the right direction. But these things take you only so far, and in order to truly learn software development, nothing beats having actual hands-on experience. Tinkling with the database. Writing the code. Configuring the platform.

This applies not just to software development, of course. Almost every skill you wish to pick up should involve actually doing them.

7. Don't think there are no crocodiles just because the water is calm.

This piece of wisdom from Malawi tells us that hidden threats are rife and plenty. Bugs in software are not always readily apparent. Just because the system performs as expected now does not mean that it will perform as expected in another environment.

Some bugs are terrifying in the sense that they do not truly manifest until the software is in actual use. And by that time, fixing the problem can be expensive in terms of both time and effort.

Terrifyingly calm waters.

Therefore, when writing software, we should treat our product as the water, and potential bugs as the crocodiles. Always assume the worst, and prepare for it.


8. You have little power over what's not yours.

From Zimbwabwe, we have a reminder not to worry overmuch about things that are out of our control.

Software developers are obsessed about control in our working environment. Control over the environment is what is required for things to run smoothly. Within the systems, certain variables can be nailed down. Limits are imposed. Checks are carried out. The final product is functional and efficient, at least from our point of view.

You can't control
everything.

Outside of the system, however, users act in unpredictable ways. And it is a constant source of frustration if a developer has not come to terms with this fact of life.

Thus, exercise control over what you can control, but learn to live with what you cannot.

9. The axe forgets but the tree remembers.

More wisdom from Zimbwabwe. The purpose of this saying is to remind us to be careful of how we treat people, because casual cruelty on our part may be forgotten by ourselves, but the scars will stay on in the memory of the victim. This, at first glance, has nothing to do with software development.

However, if we take the tree to symbolize the code base and our code commits to symbolize the axe, it is now easy to see the parallel. Sloppy code may be written in a hurry and is soon forgotten among the numerous things we have to accomplish within time constraints, but the code base bears the scars. And sooner or later, that technical debt has to be repaid.

The tree won't
forget this.

It is dangerous because whatever we do to the code base, given enough time, will be forgotten. And when the time comes for us to revisit what we have done, it could be very difficult to remember the thought process behind that particular "axing".


10. Don't be so in love that you can't tell when it's raining.

This proverb hails from Madagascar, and it is about the dangers of blind passion - when one is so in love that they cannot tell when the situation is dire.

Be careful; it's raining.

In software development, one can have a case of tunnel vision from working too long on a project. This can lead to blind spots, especially if we are so enamored of an idea that we refuse to consider sensible alternatives, or acknowledge glaring flaws in the plan.

One minor example would be being so attached to a certain framework or programming language that you are unable to see that it is unsuitable for the work at hand. A good software developer is not enslaved by his tools.

Conclusion

African proverbs are fascinating, aren't they? Especially when you consider that, once translated, they really are not all that different from the proverbs we hear in our own languages. Our ancestors may have come from different places, but they appear to have had very similar ideas.

Pursue that elephant, and don't forget to cut it up into little pieces!
T___T