This is an example of how to combine a UIPageControl and a UIScrollView together to create a “snap to page”-like effect that is seen on the iPhone’s home screen. This sample is partially based on Apple’s UIPageControl example.
March 1, 2009
AUAPI: JSON to XML serialization
Here is a brief outline of how one would “naively” transform Almost Universal API‘s (AUAPI) JSON into XML. We say “naive” because in general one wants to make a transformation into a specific XML application: Atom, RSS, OPML, KML, etc.. In those cases, one has to rename and rework certain elements first for standards compliance, then complete the naive transformation for remaining elements.
Walking
Walking JSON objects is done depth first. Most of the complexity involved is in handling dictionaries, which can be valued as being comprised of ( key, value ) pairs. For each dictionary, we are creating an XML node whose properties are defined as follows:
- keys beginning with
@@are ignored - the key
@means “the text” of the node (the examples will make this more clear) - other keys beginning with
@are attributes of the node - all other keys are defining children of the node
There are number of complexities that have to be addressed; for this I suggest looking at the examples or source code.
Namespace handling
- collect all the namespaces used in the JSON and add to the root XML node
- if any JSON element has a namespace, assume that namespace is inherited by its children
Code
You can see the code for this in the AUAPI source base in api.py in XMLAPIWriter.TranscribeNode.
Example 1
{
"numbers" : [ 1, -0.23, ],
"strings" : [ "bob", "caf\xe9", ],
"booleanish" : [ True, False, None, ],
}
<root>
<numbers>1</numbers>
<numbers>-0.23</numbers>
<booleanish>True</booleanish>
<booleanish>False</booleanish>
<booleanish />
<strings>bob</strings>
<strings>caf\xc3\xa9</strings>
</root>
Example 2
{
"a1" : {
"b1" : 1,
"b2" : 2,
},
"a2" : {
"b3" : "hi",
"b4" : "there",
},
}
<root>
<a1>
<b1>1</b1>
<b2>2</b2>
</a1>
<a2>
<b4>there</b4>
<b3>hi</b3>
</a2>
</root>
Example 3
{
"@attribute" : "hello",
"@bttribute" : "there",
"a" : "some string",
},
<root attribute="hello" bttribute="there">
<a>some string</a>
</root>
December 23, 2008
Interact with applications from the command line on the Mac
I’m a command line guy – I spend 90% of my non-blog reading day in Terminal, working on Python apps on my Mac or SSHed into work and working on Java and Javascript applications. I do realize the benefit of “real” applications, for image editing, for advanced text processing and so forth. On the Mac you can send files to the default application easily:
open "Madeline Doll House.jpg"
(Don’t ask). If the Mac doesn’t know how to deal with the file type, or you want to specify a particular app, that’s cool too:
open -a smultron index.jd
Note that it doesn’t matter that I’m SSHed into a work computer – we got around that issue last week using MacFUSE.
December 11, 2008
Mount NTFS and remote filesystems using MacFUSE
Earlier this week I bought a LaCie 500Gb USB drive so I could bring VMWare images between work and home. When I went to copy the image, the copy failed with no meaningful error message (Error 0, I believe). Trying the copy on the command line was a little more informative: as it turns out, the LaCie drive ships with a FAT-32 file system which can only handle files up to 4Gb in size. As the image I was trying to copy had a 8Gb file in it, this was a no go.
My initial thought was to use the UNIX commands tar and split to break the files into individual smaller chunks, but this is hardly a satisfactory answer. If I formatted the drive to the Mac filesystem, the Windows machines would not be able to read it at all. If I formatted the drive the “new” NTFS filesystem, Windows can read and write just fine but the Macintosh wouldn’t be able to write to it.
Fortunately, there’s an excellent install for the Mac called MacFUSE that allows access to all sorts of filesystem types not natively supported by the Macintosh, include NTFS. Here’s how I set up MacFUSE.
MacFUSE Installation
- go to http://code.google.com/p/macfuse/
- download the latest version
- install
- reboot
Installation by itself does nothing except set you up for the next stage: installing drivers for particular file systems.
NTFS
You have to search through the documentation for a bit to figure out where to get NTFS to with Windows filesystems. It actually turns out to be rather easy:
- go to http://macntfs-3g.blogspot.com/
- look for the latest NTFS-3G [stable] release
- click on the link, download and install the package
- reboot
You can now write to NTFS drives. It’s a little slow – it’s taking me about 2 hours to copy 8Gb to the La Cie drive, but that’s better than not being able to do it at all. You wouldn’t want to work live off the drive however, and it may be worth investigating commercial NTFS compatibility applications if you need to do this.
To reformat your La Cie drive plotline, use Applications > Disk Utility to erase and install an empty NTFS file system.
SSHFS
SSHFS lets you see remote filesystems through SSH.
- go to http://code.google.com/p/macfuse/wiki/MACFUSE_FS_SSHFS
- download the version appropriate to your Mac; you can store this in your home directory or if you’re a little more organized about your path, a directory link
~/bin - make a mount point – this is just a directory on your Mac that is needed by MacFUSE; it can be hidden as Mac OS will show you the mounted drive on your desktop and in
/Volumes. For example, on the command line runmkdir -p ~/.Volumes/Remote. - run the mount command; you’ll be prompted for your remote system password
You’ll see the drive appearing on your desktop. I’ve actually created a shell alias to do the mounting for me called “mount-xxx”. If you don’t know how to do this, it’s probably too much to go into right now.
The nice thing about SSHFS is that I could see being able to run an entire Mac desktop development shop with all the backend computing running Linux, all being accessed nicely through SSHFS.
November 27, 2008
How to dynamically load Python code
The normal way to load Python code is through the import statement:
import pprint
pprint.pprint('Hello, world.')
But what do you do if you want to dynamically load a module? A classic example of where you’d like to do this is adding ‘extensions’ to your application. Your application has no way of knowing the exact name of the module that it’s going to use; it only knows the filename(s). The way to do this is the imp module:
import md5
import os.path
import imp
import traceback
def load_module(code_path):
try:
try:
code_dir = os.path.dirname(code_path)
code_file = os.path.basename(code_path)
fin = open(code_path, 'rb')
return imp.load_source(md5.new(code_path).hexdigest(), code_path, fin)
finally:
try: fin.close()
except: pass
except ImportError, x:
traceback.print_exc(file = sys.stderr)
raise
except:
traceback.print_exc(file = sys.stderr)
raise
A few notes:
- call
load_modulewith the path to a.pyfile that you want to load - the
md5.newgenerates a unique module identifier. If you don’t do this it’s difficult to import two modules in different directories with the same name! - the different
excepts are to give you a flavor of the issues you may see,ImportErroris expected, the others are not
The return value is a module, which is a Python object that you can address in all the normal ways that you’d use a module. For example, if you have the following file extension.py:
def hello(x): print "Hello, %s" % x
You can use it as follows to get Hello, world.
m = load_module('extension.py')
m.hello("World")
November 25, 2008
The "Anything Goes" Pattern
Here’s a Python code pattern that I find myself falling into every once in awhile. If you’re a highly disciplined milspec-type non-pragmatic programmer, I suggest you stop reading here lest you burn your eyes.
The patterm useful in two situations:
- when you have an evolving superclass that may take new constructor (or method!) arguments in the future and you don’t want to have to recode your subclasses to reflect those changes
- you have a number of interchangeable subclasses that may or may not use certain arguments (say, because you’re constructing the object from a command line)
class Component:
def __init__(self, a, b = None, *av, **ad):
...
class ComponentTemplate(Component):
def __init__(self, *av, **ad):
Component.__init__(self, *av, **ad)
a and b are two arguments that are being used by superclass. With this pattern you can add c to Component in the future without worrying about rewriting ComponentTemplate. Similarly, if an unexpected argument is passed down to Component, it will be silently ignored.
In case you’re wondering what *av and **ad are, they’re Python’s way of referring to arguments that have been passed in, by position and by name, but have not been explicitly listed in the method’s signature. The first is a list and the second a dictionary. If you’re a Python user and you’re not familiar with this, you can and should read more about this here.
November 20, 2008
Use unittest
When developing Python code there’s a tendency to do add a __main__ section to test the code:
def add(a, b):
return a + b
if __name__ == '__main__':
print add(3, 4)
Don’t. Python has a great little package called unittest that let’s you quickly frame functions in testcases.
If the example above is called add.py, I’ll generally make a subdirectory called tests and add a test program called test_add.py. This can be as simple as:
import unittest
class TestAdd(unittest.TestCase):
def setUp(self):
pass
def test_1(self):
self.assertEqual(add(3, 4), 7)
self.assertEqual(add(4, 4), 8)
self.assertEqual(add(4, -4), 0)
if __name__ == '__main__':
unittest.main()
But I prefer to use the following pattern:
class TestAdd(unittest.TestCase):
def test_add(self):
checkds = [
{
"a" : 4,
"b" : 3,
"@result": 7
},
{
"a" : 4,
"b" : 4,
"@result": 8
},
{
"a" : 4,
"b" : -4,
"@result": 0
},
]
for checkd in checkds:
expected_result = checkd.pop("@result")
actual_result = add(**checkd)
if expected_result == -1:
print checkd, actual_result
continue
try:
self.assertEqual(expected_result, actual_result)
except:
print checkd, actual_result
raise
In particular:
- the individual tests are defined in the checkds list of dictionaries
- the bottom part (the
forloop) is boilerplate- it removes the
@resultfrom the dictionary - it calls
addwith the remaining dictionary - and it then asserts that the
actual_resultwas the same as theexpected_result
- it removes the
- if the
expected_resultis -1, it doesn’t run the test, it just prints theactual_result. This is great for setting up your tests in the first place. Obviously you might way to change this marker for testing functions that can return -1, but you get the idea
The advantage of using unittest like is that you’re now not depending on visual inspection or remembering which files you put a __main__ in to test your code. As a secondary benefit, unittest helps you think about edge cases, how other people might call your code.
Just go to your test directory and run them all and you’ll be sure your libraries are behaving as designed.
November 14, 2008
How to make your blog readable on an iPhone
Here’s the following changes I made to this blog to make it readable on an iPhone
Add iPhone directives to header
- the META tag informas the iPhone about how wide we want the page to look – i.e. the width of the iPhone
- the LINK tag loads our iPhone specific CSS (tip from here)
- this should be after your normal CSS LINK (or whatever) directive
<meta name="viewport" content="width=320" /> <link rel="stylesheet" type="text/css" media="only screen and (max-device-width: 480px)" href="/blog/wp-content/themes/davidjanes/iphone.css" />
Define iPhone specific CSS
- this will obviously vary depending on what your current CSS does – that still gets loaded
- most of this was pragmatically discovered
- I hide several sections which I don’t think the user wants to see on an iPhone; I’ll probably play with this more
- the PRE directive doesn’t use line breaking, so we just clip the examples, after making sure the font is small enough to get enough on a single line
body {
padding: 5px;
width: 480px;
}
div#content {
float: none;
}
div#menu {
display: none;
}
p.credit {
display: none;
}
pre {
overflow: hidden;
font-size: 10px !important;
}
h1, h1 * {
font-size: 36px;
}
What still needs to be done
- we shouldn’t serve sections that users are not going to see – it’s a waste of bandwidth
- we shouldn’t serve more than 10 articles
- I’ll have to figure out how to do mobile browser detection on WordPress
How to enable/disable Mouse Wheel actions on your map
All the major map APIs have the ability to zoom in and out if your pointer is over the map and you scroll the mouse wheel. Being able to disable this function if you’re working in a small popup form window is very important!
Google Maps
By default, this feature is disabled. To enable:
map.enableScrollWheelZoom();
To disable (again):
map.disableScrollWheelZoom();
Yahoo Maps
By default, this feature is enabled. To disable:
map.disableKeyControls()
There doesn’t appear to be a way to re-enable afterward.
Microsoft Virtual Earth
By default, this feature is enabled. To disable you have to capture the event:
trap = function() { return true; }
map.AttachEvent("onmousewheel", trap);
To re-enable, you have to detach the exact same event (hence the trap function)
map.DetachEvent("onmousewheel", trap);
November 10, 2008
Syntax Error on Line 1
Here’s a nasty little error that I tracked down on IE6 this morning. The error message (click on the icon in the lower left of the browser) is something like Error: Syntax Error; Code 0; Line: 1.
Of course, when you go to look for the error in your Javascript, nothing seems to be wrong. One explanation I found for this was that a BODY onload tag is broken. In our case however this wasn’t the issue.
Instead it was really something quite simple: we were using a SCRIPT tag to download a Javascript file that no longer existed and was 302 redirecting to an HTML page! The IE6 Javascript engine then attempted to execute the HTML and of course immediately failed. In our case, this happened because we were getting a partner widget and they had changed the URL without notifying us. A 404 error would have been more appropriate, though I have not tested to see whether the same error would then occur.
One further note: if you’re debugging scripts on IE6:
- download this Microsoft tool
- enable it (Tools > Internet Options | Advanced; uncheck Disable Script Debugging)
- restart IE