« October 2010 | Main | January 2011 »
December 29, 2010
Rediscovering Jack L. Chalker
There are a number of books that I read throughout my early life that I now remember only vaguely through sketchy plot details, small bits of characters or perhaps even an inclination of what the front cover illustration looked like. It's a pleasant surprise when I stumble on one of these books and I'm afforded the opportunity to re-read them and examine my earlier impressions.
----
A few weeks ago I took my oldest daughter to a used book store. We weren't looking for anything in particular, she just hadn't been in one before and I wanted her to see that there was a wealth of interesting literary treasure awaiting us. It's nice to have an hour of free time now and then with your children for unscheduled outings like this.
We walked in and I set her loose in the place after giving her a gentle nudge to the section I thought she'd be interested in, but encouraged her to wander around. Then I took off in a different direction.... I'd been reading a lot of spy and suspense novels lately, and wanted to get back to science fiction for a while. So I started at the beginning of the section with authors starting with "A", smiling as I skimmed past the Isaac Asimov titles, and continued into the "B" and "C" authors. I stopped skimming when I came to "Chalker", because my brain had picked up on the phrase "well world", a subtitle to one of the books. I grabbed the book in front of me and gazed in wonder at "Exiles at the Well of Souls" by Jack L. Chalker. I was excited to remember that I had read this book originally around 1980, and even happier to find that it was part of a series. I looked for the rest in the series, but could only locate 3 out of 5 in this book store. Critically, the first book in the series was not to be found.
It's astounding to me that my brain could pick up on that two-word phrase "well world". It had been 30 years since reading that book, and I was reading a LOT of books around that time. The author's name didn't seem familiar to me, but admittedly I wasn't paying much attention to authors' names when I was nine years old. Now I actually do talk with my daughters about specific authors (and my oldest is wondering whether Cressida Cowell has run out of ideas for her How To Train Your Dragon series...)
I ended up using eBay to order used copies of the first and fourth books in the Saga of the Well World series, and patiently waited for them to arrive. After just finishing the fifth book yesterday, I'm happy to have rediscovered the series and Jack L. Chalker. I'll be looking for more of his work shortly.
Posted by Hammer at 12:52 AM | Comments (2)
December 18, 2010
Meeting The Man Behind Wannawafel
I think it was one of those "stars were aligned" sequence of events that lead me and my wife to meeting up with Renaat Marchand, the entrepreneur behind Wannawafel.
----
It started September 22, 2010, when my wife and I were relaxing after getting the kids to sleep, watching the Dragon's Den on CBC. What a great show, with benefits for everyone: the Dragons get to screen tons of offers they might not have otherwise ever seen, the entrepreneurs get their pitch aired on national television (even if they don't get a deal, they get the exposure), CBC has a show that doesn't need a lot of script writers (at least compared to a sitcom), and viewers like us get some solid entertainment in the form of some really bad ideas being shot down. Bring it on!
As we watched that episode, the story of Wannawafel caught our attention. The owner of a small Belgian waffle business, Renaat, wanted to expand his business into a franchise. We talked a lot about that pitch during the commercial break, about how real and honest Renaat seemed to be, how good his idea was, and how very tasty those darned waffles looked. I'm pretty sure we both needed to grab a snack shortly thereafter.
I talked with other people at work the next day, and found that many of my co-workers had felt the same way about the Wannawafel pitch. They couldn't wait until someone started one up locally! They wanted to try some, too.
Fast forward a week and a half to September 25. I was attending a work conference in Victoria, BC, and my wife came along for the trip. We were doing some shopping before the conference, just wandering around aimlessly in downtown Victoria. We were doing a lot of talking as we walked, and she asked, "wasn't that waffle guy on Dragon's Den from Victoria?" I replied that I thought he was, and we reminisced about the episode as we continued to walk.
About 20 minutes later, we literally stumbled upon the Wannawafel store in Market Square. Honestly it was the Lululemon sign up the block that caught my wife's eye, not Market Square. We were both amazed, it was very much a "deja vu" type of feeling, having just minutes before laughed at how ironic it would be if we ran into the place.
We ventured into the store around 7:30 pm. There was some sort of street fair going on, and we had to explain to the ticket sellers that we were only interested in buying a waffle. They let us pass without a problem, and we found the one and only Renaat right there in the store. He was eating sushi from a place just down the stairs, enjoying a very late supper, while his daughter Dana was taking care of the counter. We said hello, and told Renaat that we recognized him from Dragon's Den, and were there to sample a waffle.
We tried to let him eat his supper in peace, but truthfully we couldn't have stopped him talking if we tried! He is a passionate storyteller, and obviously wasn't tired of telling his story yet, so we sat down and talked with him while waiting for Dana to cook up the waffle. He went into a lot of detail about his move from Belgium to Canada, his woodwork in Sooke, BC, and eventually about his waffle cart in Victoria. It's a story that is far from finished, and yet there's already a lot for him to tell.
When the waffle was served up, we split it because we had a huge supper ourselves already. Both of us in turn started to gush over how good the waffle was. It really was superb. Renaat then launched into the details of the waffle dough, and how we really should go to Belgium some time so we could try some with alcohol in it (was it rum? I forget). He said the waffles were properly named "Liege Waffles", but had to be marketed as "Belgium Waffles" to us in Canada.
Dana was kind enough to snap our picture between her kitchen duties:

As we were talking, I noticed that not only was Renaat sporting one of his bright yellow "Wannawafel" shirts, but he also had the T-shirts for sale, hanging in the corner of the store. I asked if I could buy one, and although he wasn't sure if I was serious at first, eventually a shirt was offered up and I gladly parted with $20.
We signed the Wannawafel guest book, and bid farewell to Renaat and Dana for the night. We ended up coming back three more times during our stay, the waffles just could not be passed up.
Thanks Renaat, and good luck to you.
There is a funny epilogue to this story: a while later, on Saturday, November 27, I was in Edmonton for the Grey Cup. We were out at a local bar and I happened to be wearing my bright yellow Wannawafel shirt. I felt a tap on my shoulder, and someone saying, "excuse me, but are you from Victoria?"
"No, I'm from Saskatchewan," I replied, "why's that?"
"Oh, the back of your shirt says Victoria, BC. I'm from Victoria."
"Aha! Well take a look at the front - have you ever been to Wannawafel?"
"No."
"Then when you get back to Victoria, you have to go! It's a great little place in Market Square, just under a set of stairs. Awesome waffles, just to die for!"
There you go, Renaat. Hopefully that was another customer coming your way. :)
Posted by Hammer at 12:21 AM | Comments (0)
December 10, 2010
Titanium Tutorial - Tableview from a Database
This tutorial is about creating a iPhone mobile application in Titanium that uses a local database (the database is on the device). Data will be displayed in tableview format from two related tables.
----
Use it for reference and background reading, however do not try to follow the instructions for creating and compiling the project, it probably won't work for you. Instead, read through this and then proceed to: Titanium Tutorial 2j: Database Sync with JSON.
You must already have downloaded and installed Titanium, and also Xcode tools for iPhone development.
The database technology used on the device is SQLite. If you've used other SQL databases in the past, the biggest thing to learn about SQlite is its lack of rigid data types. In preparation for this project, you need way to create and maintain a SQLite database. The tool used for this tutorial is the SQLite extension for Firefox. You should download and install that extension now. When you've done that, you will have a new menu item under "Tools", like this:

Step 1: Create A New Titanium Application
Open Titanium and click on New Project. Fill in the form so it looks similar to this:
Then click Create Project. Make sure you note where you are creating your new application. After clicking Create Project, go find the new folder that just got created for your application. Inside the application folder there should be a new folder named Resources:

Step 2: Create The Database File
In the Firefox Tools menu, click on SQLite Manager. Click on "New Database". It will ask you for a database name: call it "content". It will next ask you where to save your database; choose the Resources folder that you found in step 1 above. You should now be able to look in the Resources folder and see a file named "content.sqlite". If you do not see this file, then try again, you cannot proceed until the database file is created in the Resources folder.Step 3: Create the Tables
The SQLite Manager should still be open from step 2. If not, open it again. Paste this SQL into the top right window:
create table categories (
category_id INTEGER PRIMARY KEY,
category_name TEXT
);
create table items (
item_id INTEGER PRIMARY KEY,
category_id INTEGER,
item_name TEXT,
item_description TEXT
);
Then click the Run SQL button. You should see a message "not an error", and then on the left hand side two new tables will show up in the list, like this:
Step 3: Populate the Tables with Data
This new database needs some data in it to be useful. Erase the SQL currently in the top right execution window, and then copy/paste the following SQL:
insert into categories(category_id,category_name) values (1,'Food');
insert into categories(category_id,category_name) values (2,'Shapes');
insert into categories(category_id,category_name) values (3,'Colors');
insert into items(category_id,item_name,item_description)
values (1,'Apples','Apples are usually red, green or yellow.');
insert into items(category_id,item_name,item_description)
values (1,'Oranges','Oranges are easy to peel.');
insert into items(category_id,item_name,item_description)
values (1,'Bananas','Bananas make great toy telephones.');
insert into items(category_id,item_name,item_description)
values (2,'Squares','Squares have four sides.');
insert into items(category_id,item_name,item_description)
values (2,'Circles','Circles are quite round.');
insert into items(category_id,item_name,item_description)
values (2,'Triangles','Triangles do not roll very well.');
insert into items(category_id,item_name,item_description)
values (3,'Blue','Blue is a soothing color.');
insert into items(category_id,item_name,item_description)
values (3,'Purple','Aubergine is deep purple.');
insert into items(category_id,item_name,item_description)
values (3,'Red','Wave some red in front of a bull today.');
insert into items(category_id,item_name,item_description)
values (3,'Green','Green vegetables are good for your kids.');
Again click the Run SQL button. Not much should appear to happen. Although it is again displaying "not an error", you can't tell because the message didn't change. So in order to verify that the tables are populated with data, click on the items table on the left and then click on the Browse & Search tab. You should see the table data:
Step 4: Code the Application
I'm not a fan of tutorials that give me the program code a chunk at a time, so here are the files that you will need to download (right-click and save). Please download the zip file and then unzip and move the contents of the zip into your project Resources directory. Do not create a Resources folder under your existing Resources folder. When you're done, the directory should now look like this:
The Resources folder now contains an updated app.js, two new Javascript files, and two new images - on top of all the files that Titanium created for you by default.
Step 5: Compile and Run the Application
Before you look at what the code is doing, let's see if it will run. In Titanium, click the Test & Package tab. Wait for Titanium to locate your iPhone development environment... eventually the little box to the right of "SDK" should be filled in with a number like 4.1 (the version of XCode tools). After that, click on "Launch" and wait patiently. The iPhone simulator will open and the application should run, resulting in this:
Step 6: Examine the Code
Using your favorite text editor, open app.js. If you have looked at the code for the default project already, there are two new things in this code:1. Initializing of the database
// Initialize database
Titanium.Database.install('content.sqlite','contentDB');
Notice that the first parameter, the name of the SQLite file, matches the file name you used above in creating the database.
2. The use of Ti.App.Properties to store the user's confuration setting for font size.
Looking at tab_config.js next, you can see how a picker is used to allow the use to select the font size. This font size property is used when rendering the item description.
Finally the heart of the application is found in tab_categories.js.
This is the code that opens the database and loads the categories into an array:
if (categoryArray.length == 0) {
var db = Titanium.Database.open('contentDB');
var dbrows = db.execute('select category_id,
category_name from categories order by category_name asc');
while (dbrows.isValidRow()) {
categoryArray.push({
catid:dbrows.fieldByName('category_id'),
title:dbrows.fieldByName('category_name')
});
Ti.API.info("Found category: "+dbrows.fieldByName('category_name')+
" ["+dbrows.fieldByName('category_id')+"]");
dbrows.next();
}
dbrows.close();
db.close();
}
Because of the first line, which checks to see if the array is empty, this code will only be executed once (the first time the user renders this tab). After that, because the variable categoryArray is local to this Javascript context and is not overwritten at run time, the array data will simply be re-used on the second and later times that the user renders this tab. This is good because we know that the category array will never change while the application is running - simply because we designed it that way.
If you watch the Titanium console while the application starts up, you will see some debugging information like this:
[INFO] Found category: Colors [3]
[INFO] Found category: Food [1]
[INFO] Found category: Shapes [2]
It's useful to put such debugging statements into your code so you can verify what is going on inside the application while it's running.
When a category is clicked (touched) by the user, then the function showItemsInCategory is called and the item list is displayed in a new table view. Titanium handles creating the "back" button type of navigation for you (in the top left you should see a "Categories" button). So all you need to do is render the item rows. Here is the code that loads the data for the item rows:
var itemArray = [];
Ti.API.info("-> "+cname+" <- clicked");
// populate item array from database
// called every time a category row is clicked
var db = Titanium.Database.open('contentDB');
var dbrows = db.execute('select item_id,item_name,
item_description from items where category_id=? order by item_name asc',cid);
while (dbrows.isValidRow()) {
itemArray.push({
item_id:dbrows.fieldByName('item_id'),
title:dbrows.fieldByName('item_name'),
item_description:dbrows.fieldByName('item_description')
});
Ti.API.info("Found item: "+dbrows.fieldByName('item_name')
+" ["+dbrows.fieldByName('item_id')+"]");
dbrows.next();
}
dbrows.close();
db.close();
Again in the Titanium console you should see more debugging information. You can verify for yourself that each time you click into a different category the database is queried and the item data is refreshed.
Note the SQL placeholder "?" being used in this query. Titanium substitutes the value of cid into the placeholder at run time, so that the user will see only the items for the category ID that they selected.
Finally clicking on an item brings up a new window with the item description. Here a small helper function composeItemHTML is used to render the item description. The database could actually hold HTML instead of text descriptions, so far more detailed and nice descriptions could be used.
If you're interested in seeing a real life example of this that you can download, check out Just Clean Jokes. It uses the ideas from this tutorial plus others.
To see all of the applications I have made with Titanium, please visit: http://www.prairiewest.net/applications.php.
UPDATE: after reading this tutorial, you should look at Tutorial 2j Titanium mobile database synchronization with JSON.
Posted by Hammer at 01:05 PM | Comments (18)
December 08, 2010
Wizard's Orb
Check out this strategy puzzle game for the iPhone.
There are some promo codes available to download it for free:
7HFERFYFP9KA
FFHY6JLW3Y46
NAANKHRHYWTK
MEN33KRLLHF3
JAHHT3XLYHRA
AJNN477EHNW6
AKL4X6AMT7NJ
HFP94PJYEXHP
These will only work once each, so when they're gone that's it. :)
Posted by Hammer at 02:27 PM | Comments (0)
