logo

ShrimpWorks

// why am I so n00b?

Here’s a couscous salad recipe I used recently, made by stealing ideas from about 3 other recipes and tweaking a bit of customising. Would eat again :-)

Ingredients:

  • 1 cup couscous
  • 1 cup water
  • 200g cherry tomatoes
  • 1/2 cup spring onion
  • 1/4 red bell pepper
  • 1/4 yellow bell pepper
  • 1/4 green bell pepper
  • 1/2 cup crumbed feta
  • Dressing:
    • 75ml canola or olive oil
    • 25ml lemon juice
    • freshly ground salt and pepper

Directions:

  1. Place couscous in a sealable bowl, boil the water, then add boiling water to couscous. Cover and allow to sit for 5 to 10 minutes. Once ready, fluff with a fork.
  2. Cut tomatoes in half, finely chop peppers and spring onions, and mix into couscous.
  3. Dressing: Thoroughly mix together oil and lemon juice, and mix in salt and pepper as required.
  4. Before serving, mix in feta and dressing.

This is a republishing, with some minor tweaks and alternatives provided, of the original recipe by Andrew Muller. I could not find some of the ingredients in local shops, so had to slightly tweak things.

I’d also recommend against using paper muffin cups, as the first two tray-fulls of these were so stuck to them half the muffin content ends up going to waste.

The ingredient volumes below make about 20-30 muffins depending on how big you make them.

The Recipe

  • 2 eggs
  • 1 tsp. vanilla
  • 2 cups unsweetened apple sauce
  • 1 banana, mashed
  • 1/2 cup agave (alternative: ~100ml honey mixed with ~50ml hot water to achieve similar consistency)
  • 5 cups rolled oats
  • 1/4 cup flaxseed meal (alternative: 1/2 cup wholewheat flour)
  • 1 tbsp. ground cinnamon
  • 3 tsp. baking powder
  • 1 tsp. salt
  • 2 cups milk (almond milk for dairy free)
  • Optional toppings: raisins, walnuts, almonds, chocolate chips.

Directions

  1. Preheat oven to 180 degrees celcius. Mix eggs, vanilla, apple sauce, banana & agave.
  2. In a separate bowl, mix oats, flax, cinnamon, baking powder and salt. Pour the dry ingredients into the wet ingredients. Finally, pour in milk and mix.
  3. Spray a muffin pan with baking oil or use muffin liners. Pour batter evenly into each liner. If using toppings add them onto tops of muffins now.
  4. Bake 35-40 min, test with toothpick if it comes out clean the muffins are done. Cool and enjoy, or freeze in freezer bags if you plan on storing them more than a few days.

I wanted to add a unit conversion plugin to ZOMB and would really have liked to use an off-the-shelf existing API, but because this didn’t seem to exist in a nice hosted format already - I had to make it :).

The Units API is written in PHP, and is intended to provide an extremely simple and easy-to-use HTTP API for the conversion between various units of measure. Usage documentation is available on the project’s Github page.

I’m also hosting a publicly usable version, at the following URL, so hopefully next time someone needs this they don’t need to reinvent the wheel (again, refer to documentation linked above for usage):

As an aside, this project served as my first introduction to PHPUnit for PHP unit testing, and CI is once again provided by Drone.io which has performed admirably. Design-wise, it was another exercise in defining the public-facing API before a line of code was written, which served as an excellent guide and source of documentation as I worked on it (plus, there’s no need to worry about writing documentation when you’re done :D).

zomb-web

As mentioned, I’ve resurrected an old idea, and began work on it as a bit of a learning/practice exercise. I think it’s working out rather well.

The primary application itself, hosted on GitHub here, is essentially complete, barring the ability to persist your plugin configuration (pfft, who needs to store things anyway).

Some stuff learned along the way:

API-driven development:

Designing the external-facing API (actually defining and completely documenting the exact request and response data structures, not just “there will be a request that does things and a response that looks something like X”) was a huge help. Defining the API allows you to see how the system will actually be used up-front before writing a single line of code, and allows you to easily spot gaps and shortcomings. Once done, the “user documentation” becomes the same documentation I used to implement the back-end, which made it incredibly easy.

Git:

Still learning, getting more comfortable with it. IntelliJ IDEA has excellent built-in Git support out-the-box, and although painful to use in a Windows shell (it’s basically Bash, inside cmd.exe), I’m getting more used to the Git CLI.

Free/online continuous integration:

Initially, I started off using Travis-CI. This requires you to store a “.travis.yml” file within the root of your Git repository which I was rather uncomfortable with (I don’t like “external” metadata type things hanging around in my source repository). As an alternative, I’ve switched to using Drone.io, which just “feels” like a nicer solution. It also has additional features like the ability to store artefacts for download, or publish your artefacts to external services or servers - so you could have successful builds automatically deploy the latest binaries.

Persistence/Storage:

Persistence is hard, so once you start a service up, it should run indefinitely so you never need to write anything to disk. Sigh. Also, this part was not designed at all up-front, and my flailing around trying to get a workable solution is evidence of the need for proper design and planning before jumping in with code.

Aside from that, there are additional projects which were spawned:

zomb-web

The first front-end for ZOMB. A simple single-page HTML UI. Had some good practice remembering how to HTML and Javascript here…

zomb-plugins

A growing collection of plugins for ZOMB. At present, they’re all PHP (again, refreshing old skills…) and pretty simple. Currently, there’s time (simple current time/time-zone conversion), lastfm (see what someone’s currently listening to, find similar artists), weather (current and forecast conditions for a given city) and currency (simple currency conversion).

None of the above cannot be achieved without a simple web search, so next up I’d like to create a CLI client - weather updates in your terminal!

Yes indeed, I am posting a recipe now.

This recipe is a combination of a few, and some customisation.

Cheese Sauce - Ingredients:

  • 2 Tbsp margarine
  • 2.5 Tbsp flour
  • 1 cup cream
  • 1 cup milk
  • 1 cup grated mature cheddar cheese
  • 1 tsp cayenne pepper (optional)

Cheese Sauce - Method:

  1. Melt the margarine in a saucepan until it starts to bubble gently
  2. Slowly mix in the flour, ensuring it’s thoroughly smooth
  3. Very slowly, bit-by-bit, mix in the cream and milk, keeping the mixture as smooth as possible
  4. Once all the milk is in, you should now have a smooth creamy white sauce. Mix the the cheese and stir until melted
  5. At this point, you may optionally stir in the cayenne pepper, or other spices of your preference

The same cheese sauce can be used for making macaroni and cheese :).

Potato Bake - Ingredients:

  • 5-6 medium-sized potatoes
  • 0.5 cup onion, sliced
  • 0.5 cup grated cheese
  • Freshly ground salt and pepper

Potato Bake - Method:

  1. Pre-heat oven to 180 degrees Celsius
  2. Wash and slice potatoes (peeling optional - I did not peel them) into 0.5-1cm thick slices, and cover the bottom of a casserole dish with one layer
  3. Place a third of the onion slices on top of the potato slices and add salt and pepper
  4. Pour out a third of the cheese sauce over the onions and potato
  5. Repeat layering an additional 2 times, and top with grated cheese over the final layer of cheese sauce
  6. Place in the oven and bake for 1 hour

DONE. Now stuff your face!

(Re-)Introducing ZOMB, an IRC bot back-end, which I planned, started work on some years ago, then promptly lost interest after it became vagely usable.

The general idea of ZOMB (like “zomg”, but it’s a bot, not a god [maybe version 3], and it sounds like “zombie” which is cool too) is to provide a client interface-independent bot framework, where bot functionality can be implemented in remotely hosted plugin scripts/applications, unlike a traditional bot where normally you’d need all the code running on one user’s machine/server.

Being interface-independent means a ZOMB client (the thing a user will use to interact with ZOMB) may be an IRC bot, a CLI application, or a web page. Since I’ve been less active on IRC than I’d like lately, the additional options would be useful to me personally, but since almost nobody uses IRC at all any more, ZOMB should hopefully be useful outside of that context.

So how does ZOMB work? From a user’s point of view, it’s exactly like a traditional bot - you issue a query consisiting of the plugin you want to execute, the command to call, along with command arguments. For example, you’d ask a ZOMB bot:

> weather current johannesburg

Where “weather” is the plugin, “current” is a command provided by the weather plugin, and “johannesburg” is an argument. In response to this, ZOMB would provide you a short text result, something like this:

> The weather in Johannesburg is currently 22 degrees and partly cloudy

In the background, ZOMB looked at the input, found that it knew of the “weather” plugin, and made an HTTP request to the remote plugin service passing the command and arguments along. The plugin then did what it needed to do to resolve the current weather conditions for Johannesburg, and returned a result, which ZOMB in turn returned to the requesting client.

As always, a new project provides some practice/learning opportunities:

  • API driven development I know what I want ZOMB to be able to to, so I began by defining the client and plugin APIs, around which the rest of the design must fit. I normally write a bunch of code, then stick an API on top of it, but trying it the other way around this time. Seems to be working.
  • Test driven development just to keep practicing :-)
  • Git and Github since we’re hopefully going to be using Git at work in the near future, best to get some practice in.
  • Custom Ant and Ivy build scripts I like Ant and Ivy and need to practice configuring and maintaining them from scratch.
  • Travis-CI continuous integration for Github projects, since it’s cool to have a green light somewhere showing that stuff’s not broken, and I’ve never used any CI stuff outside of work.
  • More granular commits committing smaller changes more often - I don’t know if this is a good thing or not, but seeing how it works out
  • All on Windows I haven’t really built a proper project on Windows for years :D

This past week, I was supposed to be visiting Mpumalanga’s Parorama Route, taking in the views around the province, and capturing all manner of awe-inspiring photos of Pinacle Rock, God’s Window, and whatever else was visible. Unfortunately, not a lot of anything was visible at all, since my visit perfectly coincided with some extremely heavy fog and rain covering the entire area.

Fortunately, I was surprised to find that I didn’t much mind missing the views, as I was entertained by something much more interesting: the roads.

The same hills and mountains which provide the (supposedly!) stunning views, also need to provide a way to get up there to experience them. Fortunately, the local government seems to have seen fit to upgrade and maintain pretty much every road in the area - no doubt as a result of the 2010 Soccer World Cup, in an attempt to attract and impress visitors to the area. This has left the rest of us with some brilliant roads to enjoy, in a relatively undertrafficed area.

To start with, coming from Gauteng along the N4, I got off the highway and onto the R36, towards Lydenburg (Mashishing). This was the worst road on the trip, covered in potholes and dilapidated patchwork. There were some amazing views, though, complimented by the odd troop of baboons along the side of the road (no, I did NOT get out to take photos).

After Lydenburg comes the R37 and Long Tom Pass. I only really expected the first section - through the Makobulaan Nature Reserve - to be interesting, and it certainly was, especially in dense fog. The steep downhill sections of sharp corners flowing corners were certainly highlights, but I was surprised to find the entire R37 to Nelspruit an extremely enjoyable drive, even with a fair amount of traffic.

The following day, I was off to try my luck at taking a peek at Pinnacle Rock and God’s Window. Back along the R37, bliss. Then off onto the R532 towards Sabie. Another amazing driving road. At Sabie, with time to kill, I decided to try out the (in)famous “Sabie 22” - the first 22-odd kilometres of the R536 between Sabie and Hazyview, primarily frequented by motorcyclists.

I’m really not sure why this route is not more popular or well known amongst the automotive community (at least, I’ve never heard of anyone mentioning it). It really is quite amazing, and I can absolutely picture overpowered R32 Skyline GTRs, S14 and S15 Silvias, FD RX7s, and even DK’s venerable AE86 having touge-style drift battles along this road. It would be a sight to behold. In fact, it would be awesome of somehow an event like the Knysna Hill Climb could be organised here, it would be a massive hit.

While completely out of place compared to all other traffic in my JDM-inspired overly-loud rice-rocket, it felt right at home on these roads.

After heading back to Sabie, we continue along the R532 towards Graskop. This section of road with it’s pine plantations in every direction as far as the eye can see really reminds me of the Stutterheim area in the Eastern Cape, an area we traveled often when I was younger. Here, the road gets really extreme. Sharp 90 degree bends with sheer drops off the side wind you up through the mountains, with the many crosses lining the roadside serving as better warning signs than any number of red and white chevrons.

Eventually, you arrive in Graskop, pass through, realise you can’t see more than 5 metres in front of the car and won’t be seeing any panoramas, visit the famous Harrie’s “The Original” Pancakes, and repeat the glorious journey in reverse, finally allowing you and your car some R&R time.

Returning to Gauteng was a mostly sedate affair, and while not as thrilling as the R37, R532 and R536, the return trip along the R539 “Highlands Meander”, through fields and fields of citrus trees, offers a completely different but equally enjoyable and very relaxing drive.

So all-in-all, did nothing I actually intended to do on this trip, and I think I enjoyed it more because of it!

After reading a lot of rants and essays about developers, their working environments, tools, the process of getting their work done (in relation to “the business” side of things), and career opportunities, I find myself wondering; do we just whine too much about it all, or do we really have to put up with so much more crap than other industries or professions?

Do we suffer from an inflated sense of entitlement - did we (and are expected to continue to) study, learn and practice for years only to end up like battery chickens, churning out code, or are we “deserving” of extra perks, privileges and financial reward?

Yes, developers are almost entirely responsible for every cent made (and even more so for every loss!) in almost every industry these days, and in most cases, I really don’t believe they get the credit they deserve (new product launched, management team praised and treated to expensive outing, golf day, or conference to show it off while we are at work hacking on the Next Big Thing). But we’re not alone in our suffering and expectations of better things.

Somewhere there’s the Accounting Drone capturing all the cents made possible by the developers. The Accounting Drone also has a shitty manager who expects them to capture more cents every day, under shitty conditions with just as shitty deadlines, for very poor pay. This guy’s job security is also near non-existent. Nobody capturing all the cents means us whiny developers don’t get paid. Maybe there are some rants and essays about the unreasonable conditions and tools the Accounting Drone must endure.

Elsewhere, there is the Sales Bro. Generally the bane of every developer’s life (worse than project managers!). It’s all very well for us to come up with the latest and greatest version of Thing2000 the world has ever seen, but I personally do not know a single developer who would be capable of selling Thing2000 on their own. Yes, we’ll all be highly indigent when Sales Bro “sells” Thing2000 Feature X before it exists, but at least someone out there actually knew Thing2000 existed and (hopefully) needs it to have Feature X (and is thus generating revenue for Accounting Drone to count and pay us with). I’m sure Sales Bro also has a blog where he complains about how he can never get his job done because developers are always so slow and uncooperative in delivering new things his customers need.

Let’s not forget Support Pleb, who has to suffer through customers rants and idiocy, while finding creative ways around developers shortcomings. I’m fairly confident a very large percentage of issues Support Pleb has to deal with day-to-day could be resolved by a bit of development time. This guy puts up with a huge amount of shit that would otherwise fall directly on developers, again, for crappy pay and non-existent job security. I’m pretty sure Support Pleb bitterly resents those developer slackers who always seem to be making more work for him, yet scatter like ants when problems are brought to their attention.

Finally we also have to have the Big Wig at the top somewhere. Those unreasonable people who are always hiring newbs and firing us, placing us in uncomfortable office environments, not shelling out for the tools we need, and dictating unreasonable deadlines. Again, unfortunately, I do not know a single developer capable of establishing and running a business the way Big Wig does. Big Wig possibly posts internet rants about how all he wants is for his Accounting Drones, Sales Bros, Support Plebs and developers to make more money for him - and doesn’t find that an unreasonable expectation, since he’s established and ensures the continuity the enterprise paying all their salaries.

So, are developer rants and whines about how bad they have it justified? For the most part, I’m going to have to say no. I’m also going to hazard that most of the problems developers face, in terms of unreasonable expectations, poor tools, lack of recognition, are not a result of simply being in software development as a profession, but are the result of poor management (at various levels of an organization), and are in fact faced by most other professions as well. We’re just more comfortable getting behind a keyboard and dumping our thoughts on the internet :).

TL;DR: As much as we like to think we might be, we’re not the be-all and end-all of our place of employment, and find a job at a company that makes you happy(-er).

So, being stuck without access to Photoshop and my regular Windows PC has taught me a little about GIMP – basically it’s exactly the same as Photoshop with a less slick UI :D.

Also, since I haven’t really written anything tutorial-ish in many many years, so this is as much about brushing up on those skills as anything else.

This is a guide for quick and simple photo enhancement using GIMP.

arrow Continue Reading ...

Some thing I’ve been using for a while, and which recently became useful at work as well, is a simple HTTP service written in plain Java with existing JRE functionality, using an HttpServer.

Here’s a simple “main()” which sets up two basic “pages”, a root (/) and one which echoes your browser’s request headers (/headers/).

public class SimpleHTTPService {

    public static void main(String[] args) throws IOException {
        HttpServer server = HttpServerProvider.provider().createHttpServer(new InetSocketAddress(8080), 0);

        server.createContext("/", new HttpHandler() {
            @Override
            public void handle(HttpExchange he) throws IOException {
                byte[] output = "Hello world!".getBytes();
                he.sendResponseHeaders(200, output.length);
                he.getResponseBody().write(output);
            }
        });

        server.createContext("/headers", new HttpHandler() {
            @Override
            public void handle(HttpExchange he) throws IOException {
                StringBuilder result = new StringBuilder("Request Headers");
                for (Entry< String, List< String>> header : he.getRequestHeaders().entrySet()) {
                    result.append(String.format("%s", header.getKey()));
                    for (String val : header.getValue()) {
                        result.append(String.format("%s", val));
                    }
                    result.append("");
                }

                byte[] output = result.toString().getBytes();
                he.sendResponseHeaders(200, output.length);
                he.getResponseBody().write(output);
            }
        });

        server.setExecutor(Executors.newCachedThreadPool());

        server.start();

        System.out.println("HTTP Listening on port " + server.getAddress().getPort());
    }
}

Running this as-is will allow you to load up the URLs http://localhost:8080/ and http://localhost:8080/headers/ and see some output generated by the two registered contexts.

I’ve defined simple anonymous inner class contexts here, as it’s easy to play with, but obviously you can go wild and develop proper structures for these.

Combined with something like FreeMarker, and you’ve got a pretty neat way to deploy simple stand-alone HTTP applications written in Java with minimal external dependencies.

It’s also extremely useful for creating mock-ups services for use in unit tests for HTTP clients.