Posts about programming

Modifying Core Types in ActionScript 3 Using the Prototype Object

ActionScript 3 has a Javascript lineage. It was essentially a fork of Ecmascript. Plus, the Adobe team has worked hard on making the language a proper superset of the Ecmascript specification. This is why although ActionScript 3 was a major architechtural change from 2, and as a result became much more like Java than Javascript, it still has support for the prototype object

When I learned about the prototype object, it was a fresh air from traditional class-based OOP. Studying cool libraries like prototype.js made me realize that the prototype model makes javascript far more flexible than some other strictly class-based OO languages.

One of the cool things you can do with the prototype object in javascript is modify the core classes of the language, like Array, String, and Date. You can do this in ActionScript 3 too, for it conforms to Ecmascript 4. For example, let's say you want to write the collect function for arrays a la ruby. You would do:
Array.prototype.collect = function(f){
  var ret = [];
  for each(var it in this) ret.push(f(it));
  return ret;
}
There you see me using the nice  for each syntax. But, this is going to break the behavior of the array, because now the collect function is going to show up in the enumeration in the for each loops. We don't want that, so to fix that we are going to set the collect attribute of Array.prototype to not be enumerable:
Array.prototype.setPropertyIsEnumerable('collect', false);
This allows you to write the following code:
[1,2,3].collect(function(i){ return i * 2; });
// result would be [2,4,6]
It's kinda tedious to have to setPropertyIsEnumerable for every time we add a function to an existing type, so I wrote a convience function:
function addMethodsTo(cls:Class, methods){
    for (var name:String in methods){
        cls.prototype[name] = methods[name];
        cls.prototype.setPropertyIsEnumerable(name, false);
    }
}
Which you can use like so:
addMethodsTo(Array, {
    collect: function(f){
        var ret = [];
        for each(var it in this) ret.push(f(it));
        return ret;
    },
    anotherMethod: function(){
        ...
    },
    ...
});
This is sort of like the style prototype.js uses for extending/creating classes.

A Word of Caution

Before you consider going further with this, I must advice you to think twice before using this technique(however, I hope you do decide to use it afterwards ;). There are several caveats:
  1. The prototype-based style is a second-class citizen in the world of ActionScript 3 and Flex. The Adobe team as well as the community seem to be much more committed to the class-based approach. I will describe some of the rough edges below.
  2. You will give up compile time type checking for the portions of your code that use this style.
  3. Prototype inheritence is handled by a completely different mechanism than class-based inheritence in the Flash VM and is not as performant.

Where to put this Code?

You saw the code example above, but, where do you put it? Since I decided to use a helper function(addMethodsTo), the code cannot be directly pasted inside the class scope of a class unless the addMethodsTo function is declared to be static(you can only make a method call directly inside class scope if it is a class method). As, a general solution, I'd rather the code be portable. So it should be includable in both class and function scope, and also, I'd like it not to pollute any namespaces.

My current solution is to put this bit of code inside an anonymous function which immediately gets executed:
// contents of includes/Array.as
(function(){
    include 'addMethodsTo.as';
    addMethodsTo(Array, {
        collect: function(f){
            var ret = [];
            for each(var it in this) ret.push(f(it));
            return ret;
        }
    });
})();
The addMethodTo function is pulled into a separate file to be easily includable else where.
So, with this, I would do the following to include this Array functionality:
include 'includes/Array.as';
Head over to Github for the complete structure of the files.

These methods are added during runtime - at exactly the time the above line of included code is executed, and not a moment before. I like to do the include at the top level of the application, this way the entire program has immediate access to the new methods. Oh, and when I said entire program, I do mean the entire program - not just the files that happen to include the file. Well, this is good and bad. This means if you create components that use the array extensions you've created without explicitly including it(you've included it at the entry point of your program), then you have created an invisible dependency. If you try to take the component and use it in a different project without the array extensions, it will not work. Of course, you could also make the dependency explicit by including the file everywhere you are using them, but 1) that's kinda tedious/repetitious, and 2) there's nothing enforcing you to do this.

Pitfalls and Gotchas

As I mentioned, the prototype-based programming style is a second-class citizen in the ActionScript 3 world, and so, its use is not particularly well supported. First of all, the compiler does not recognize any of the methods added via the prototype mechanism, and thus cannot perform static type checking on them. But what is funny is the way the compiler copes with this - it depends...on the type in question. For Arrays, the compiler simply allows all method calls - you can call any method on an array, even if it doesn't exist, the compiler won't complain. So, for a call like:
[1, 2, 3].collect(...);
The compiler won't even say a word. But for Dates, it gives a warning message. This:
new Date().format()
would trigger this warning:
Warning: format is not a recognized method of the dynamic class Date.
But, for strings, it's a different story still:
'one, two, three'.csv2Array()
compiler says:
Error: Call to a possibly undefined method csv2Array through a 
    reference with static type String.
And the same thing with numbers:
(2).minutes().ago()
compiler says:
Error: Call to a possibly undefined method minutes through a 
    reference with static type int.
The work around for strings and numbers is to upcast it to an Object:
Object('one, two, three').csv2Array();
Object(2).minutes().ago();
or if you just have an untyped variable, it'll work just fine:
var x = 2;
x.minutes().ago();
See the full code example here, and the demo here.

What about runtime error handling? So let's see what happens if you don't include the extensions and run the code. When I took out the array extension methods, the runtime dutifully throw me the:
TypeError: Error #1006: collect is not a function.
This is good, just what I would expect. For Date, it works the same way. Now let's try taking out the string extensions:
TypeError: Error #1006: value is not a function.
Uhh, what? Not really sure what you mean. And as you would expect, it works like this for Number as well.

I have also seen cases where the runtime simply completely muffles an error when there's an undefined method being tried as someone documented here. But I am not able to reproduce this just now. Also on another note, the error stacktrace from the flash player(debug version) is not very helpful because although it gives the call stack, there are no line numbers. I am sure that you'd get a better experience using Flex Builder, however.

I think that about wraps it up. Although extending core types is fun, powerful, and elegent, it's also full of holes and flying scissors everywhere. Are you ready to jump into this brave new world?

A message to the Adobe Flex/ActionScript team: I implore you to put more effort into the prototype-based side of the language. It allows for many possibilities which its class-based counterpart cannot offer. I don't dislike the class-based approach. I think the two each have their own strengths and weakness. Which is why I love this hybrid aspect of ActionScript 3 which allows me to use either style in the same environment. I believe that if the prototype-based aspect of ActionScript were to improve and get more exposure, it would not only become a better language, but also a more wide spread language.
Posted by Toby about 1 year ago about actionscript, flex, javascript, programming and prototype (11 comments)

ActiveRecord Gotcha

I came across this interesting gotcha while debugging through someone else's rails code. When you set a model's one-to-many attribute(an perhaps other association types as well), let's say:
user.friends = new_friend_list
It doesn't always set it. As far as I can figure from my blackbox testing, it does an equal check first between the old value and the new value, and then sets only if they are different.

Now, in Ruby's == is easily overridable, you can easily make it do anything you want. It happens that Array#== returns true iff both arrays have the same number of elements and each element in one array is == to the element of the other array in the same position. It also happens that ActiveRecord's base class' == returns true as long as the objects are of the same class and the primary keys equal.

Which brings us to the gotcha of the day. The code in question built an array, say new_friend_list independently, starting with an id list passed in from a from, finding each friend in the database and adding them to the array, but, in the meantime, modifying a field in each of the friends, say...friend.charma++. It then goes on to set the friends attribute in the manner you'd already seen above, and saves user. Well, the charma field that was modified in each of the friends retrieved did not get saved, because the set did not happen: the new_friend_list pointed to the same list of friends user was already associated to.

Hmm..., yeah. Watch out for this one. Debugging this one was not fun. It definitely didn't fit with the principle of least surprise.
Posted by Toby about 1 year ago about gotcha, programming, rails and ruby (0 comments)

Using the Flex-Ajax Bridge

The Flex-Ajax Bridge is a little javascript library bundled with the Flex SDK that allows you to use javascript to drive your Flex app. It can be really handy for debugging or experimentation when you use it with Firebug's console. For complete setup up instructions, see here. You do need to modify your Flex code to get the bridge to work. It's just a one-liner though.

To use it, first you get a reference to your flex app:
app = FABridge.flash.root()
To get a component by ID, you would do something like:
app.get('myDataGrid')
To find out the type of the component:
app.get('myDataGrid').typeName
You can call the methods of the component as you would in normal ActionScript. To access the component's properties though, you need to use the Java getter/setter convention:
app.get('myDataGrid').getSelectedItems()
This isn't so nice. Ideally, a seamless experience would allow you to write:
app.myDataGrid.selectedItems
I guess this is where the weakness of Javascript is coming through. There's no language support for properties(like those in ActionScript, C# Python, etc.) and also no way to do method interception(method_missing in ruby). But I digress.
To instantiate an object of a class in Flex-land, you'd do something like:
sprite = FABridge.example.create("flash.display.Sprite");
This would call the default constructor. I am not sure how you'd call a constructor with arguments, probably just add on the arguments to create()?
A really cool and useful thing you can do is pass functions as event handlers to into Flex-land. Try this:
app.get('myButton').addEventListener('click', function(e){
    console.log('button clicked!');
});
Now go click that button...cool, heh?

Anyway...in conclusion, the Flex-Ajax Bridge is really nice...especially when combined with Firebug.
Posted by Toby about 1 year ago about firebug, flex, javascript and programming (0 comments)

Different Ways of Including Functions in ActionScript

This is another example of something that would have been a blog post creeping over to SO.
Posted by Toby about 1 year ago about actionscript, flex and programming (0 comments)

Redirect 301

I was looking at Google Analytics today and found that I was getting a bunch of traffic on Use Delicious with Chrome as well as some programming/technical articles, but on the wiki.futuretoby.com domain rather than on the tobyho.com domain. 

For those of you who don't know(probably all of you, because...why would you?), this "wiki/blog" runs the on 2 separate domains, but with a different facade over each one, if you will. One is for my family/personal stuff, for which most of the content it is intended for has access restrictions. The other is the tobyho.com domain, which I created more recently and is mainly for my public content. But all of the content can be accessed in exactly the same way on either domain.

So, basically, the former domain is still more visible on the Google search engine than the latter, and so people are coming to my articles on that one, rather than where I want them to go: tobyho.com. 

How to fix it? Well, it turns out that for things like this, a 301 redirect(as supposed to a 302) is the way to go. It will tell the search engine that the content has moved permantly, and so prompt it to update its index. The code I ended up putting together was pretty simple:
    tag_names = @page.tag_names
    if request.host != 'tobyho.com' and
        @page.authors.include?(toby) and 
        not tag_names.include?('family') and 
        (tag_names.include?('programming') or 
         tag_names.include?('tech') or 
         tag_names.include?('gadgets')) then
        headers["Status"] = "301 Moved Permanently"
        redirect_to("http://tobyho.com/#{@page.title_for_link}", :status => 301)
        return
    end
So, basically, if a visitor comes to an article from the other domain, I want to redirect to the tobyho.com domain if 1) I am the author, 2) the article is tagged with one of programming, tech, and gadgets and 3) the article is not tagged with family. The line:
headers["Status"] = "301 Moved Permanently"  
Is not needed if you're on a newer version of rails, for which setting :status => 301 on the next line would suffice. I embarrassed to say that I am still on 1.2.x.
Posted by Toby about 1 year ago about programming, rails and web (0 comments)

A Custom Drag-n-Drop List Control

The List control supports drag-n-drop support out of the box - just not the way I want. What it does is allow you to drag something from another list or other type control and drop onto it there by transfering the object dragged into the list control itself. What I want is for something to be dropped onto an item in the list in question. This is how I ended up doing that.

By looking at the implementation of ListBase.as in the flex framework source code, I found that I needed to override some methods. So I create a MyList.as file which subclasses the List control. The methods I first needed to override were dragEnterHandler and dragOverHandler. Both of these methods in ListBase.as look almost identical, here is the source for dragOverHandler:
    protected function dragEnterHandler(event:DragEvent):void
    {
        if (event.isDefaultPrevented())
            return;

        lastDragEvent = event;

        if (enabled && iteratorValid && event.dragSource.hasFormat("items"))
        {
            DragManager.acceptDragDrop(this);
            DragManager.showFeedback(event.ctrlKey ? DragManager.COPY : 
		DragManager.MOVE);
            showDropFeedback(event);
            return;
        }

        hideDropFeedback(event);
        
        DragManager.showFeedback(DragManager.NONE);
    }
The showDropFeedback method draws a line in the List control that indicates where the new item would be inserted into the List. This doesn't apply to us anymore, so I overrode the showDropFeedback method, to just highlight the item under the cursor instead:
        override public function showDropFeedback(event:DragEvent):void{
            var item = findItemForDragEvent(event);
            var uid:String = itemToUID(item.data);
            if (item){
                drawItem(item, isItemSelected(item.data), true, 
			uid == caretUID);
            }
        }
Moreover, now we want to accept the drop only if the cursor is over a list item, whereas before it allowed for drops on empty parts of the List area. This what I did:
        override protected function dragEnterHandler(event:DragEvent):void{
            _dragOverHandler(event);
        }
        
        override protected function dragOverHandler(event:DragEvent):void{
            _dragOverHandler(event);
        }
        
        public function findItemForDragEvent(event:DragEvent):Object{
            var item;
            var lastItem;
            var pt:Point = new Point(event.localX, event.localY);
            pt = DisplayObject(event.target).localToGlobal(pt);
            pt = listContent.globalToLocal(pt);
            var rc:int = listItems.length;
            for (var i:int = 0; i < rc; i++)
            {
                if (listItems[i][0])
                    lastItem = listItems[i][0];

                if (rowInfo[i].y <= pt.y && 
			pt.y < rowInfo[i].y + rowInfo[i].height)
                {
                    item = listItems[i][0];
                    break;
                }
            }
            return item;
        }
        
        protected function _dragOverHandler(event:DragEvent):void{
            var item = findItemForDragEvent(event);
            if (item){
                DragManager.acceptDragDrop(this);
                DragManager.showFeedback(
			event.ctrlKey ? DragManager.COPY : 
				DragManager.MOVE);
                showDropFeedback(event);
            }else{
                DragManager.showFeedback(DragManager.NONE);
            }
        }
I wrote a findItemForDragEvent method to find the item that the cursor is currently under or null if none exist - the code was mostly stolen from the calculateDropIndex method in ListBase.as - then I accept the drop if I get a non-null value from it.
I think that's pretty much it. Here's the demo and the code.
Posted by Toby about 1 year ago about dnd, flex and programming (1 comments)

Parameterizing Your Tests

I use Selenium at work to write browser tests. I use the Python bindings with Selenium Remote Control to drive the tests. Now, once you have written a test suite, it is not unreasonable to want to run the same set of tests over different browsers. Now that the browser space is getting interesting again, you'd better test your app on 5 or 6 browsers. So how do I write my tests once, and run them on different browsers? In the Python unittest framework, tests are written as classes that extend unittest.TestCase. So, to parameterize them, you can can subclass the test dynamically and set the browser attribute onto it:
import unittest

class TestPages(unittest.TestCase):
    def testPage(self):
        print("testing page on %s" % self.browser)

TestPagesOnFirefox = type('TestPagesOnFirefox', 
                                 (TestPages,), dict(browser='firefox'))

if __name__ == '__main__':
    unittest.main()
So, we are subclassing TestPages calling it TestPagesOnFirefox and at the same time setting its browser attribute to 'firefox'.
We run it and get this error:
AttributeError: 'TestPages' object has no attribute 'browser'
This is because TestPages does not have the browser property defined. What do we do? Let's erase it:
del TestPages
After that it runs okay and we get the output we want:
testing page on firefox
.
----------------------------------------------------------------------
Ran 1 test in 0.000s

OK
So we can generalize that and expand all the browsers you want to. We write an expand_browsers function like so:
def expand_browsers(gls, browsers=['firefox', 'ie6', 'ie7', 'safari', 'chrome', 'opera']):
    original_test_classes = filter(
        lambda o: isinstance(o, type) and issubclass(o, unittest.TestCase), 
        gls.values())
    
    for cls in original_test_classes:
        for browser in browsers:
            new_class = type('%sOn%s' % (cls.__name__, browser), (cls,), dict(browser=browser))
            gls[new_class.__name__] = new_class
        del gls[cls.__name__]
After you declare all you tests you call the function like so:
expand_browsers(globals())
This goes through the namespace of your module and for each class that is a subclass of unittest.TestCase(You can choose a different subclass as marker here if you want), it subclasses it and specifies the browser property in it. After that, it erases the original testcase class so that it can't be found, like we did above.
Final complete example:
import unittest

class TestPages(unittest.TestCase):
    def testPage(self):
        print("testing page on %s" % self.browser)

class TestMorePages(unittest.TestCase):
    def testPageTwo(self):
        print("testing page two on %s" % self.browser)

def expand_browsers(gls, browsers=['firefox', 'ie6', 'ie7', 'safari', 'chrome', 'opera']):
    original_test_classes = filter(
        lambda o: isinstance(o, type) and issubclass(o, unittest.TestCase), 
        gls.values())
    
    for cls in original_test_classes:
        for browser in browsers:
            new_class = type('%sOn%s' % (cls.__name__, browser), (cls,), dict(browser=browser))
            gls[new_class.__name__] = new_class
        del gls[cls.__name__]

expand_browsers(globals())

if __name__ == '__main__':
    unittest.main()
Output:
.testing page two on firefox
.testing page two on ie6
.testing page two on ie7
.testing page two on opera
.testing page two on safari
.testing page on chrome
.testing page on firefox
.testing page on ie6
.testing page on ie7
.testing page on opera
.testing page on safari
.
----------------------------------------------------------------------
Ran 12 tests in 0.000s

OK

Code examples also on github.
Posted by Toby about 1 year ago about programming, python and testing (0 comments)

Property Binding in Flex

I have been working more with Flex now, and I have to say: the data binding features(covered here) in Flex is beautiful. It's light years ahead of anything I've ever seen before. 

Example 1: You want a Text element to show the full name of current selected state from a drop down: 
    <List id="stateList" labelField="abrv">
        <ArrayCollection>
            <Object abrv="NJ" full="New Jersey"/>
            <Object abrv="MA" full="Massachusetts"/>
            <Object abrv="NY" full="New York"/>
            <Object abrv="NC" full="North Carolina"/>
        </ArrayCollection>
    </List>
    <Text text="{stateList.selectedItem.full}"/>
The code in the curly braces is some ActionScript code, but it doesn't just get executed once, oh no, you can look at it as an invariant, i.e. the content of the said text element will always contain the value expressed by stateList.selectedItem.full. Therefore, when you change the selection of stateList, the text element will immediately change with it. Here's the code and live demo for example 1.

Example 2: Let's try binding to a custom variable rather than to another control. We first make our counter variable public and bindable:
    <Script>
    [Bindable]
    public var counter:int = 0;
Then, we can bind a text control to it:
    <Text text="{counter}"/>
Next, we can periodically increment the variable like so:
        setInterval(function(){
            counter++;
        }, 1000);
And you will see the number in the text display count up: the display in the text element is syncronized with the actually value of counter. The code and live demo for example 2.

Next, let's try creating our own properties. ActionScript has support for Java-style OOP(as supposed to Javascript style), but, more interestingly, it's got direct support for properties. Add direct support of property change event bindings, and it's a beautiful thing. 
Example 3: You have a Page object that manages the details of paginating a set of data, and you need some controls to navigate between the pages as well as display what page you are on. You create a Page object:
    class Page extends EventDispatcher{
        public var number:int;
        public var size:int;
        public var total:int;
        ...
    }
A page has a page number, a page size, and the total size of the data set(used to tell whether a page exists). I instantiate a Page object:
    <Script>
    [Bindable]
    public var currentPage:Page = new Page(0, 10, 100);
    </Script>
I need a label that tells me the range of results currently being viewed:
    <Label text="{currentPage.from} to {currentPage.to}"/>
Here, the label binds to 2 properties: from and to. Let's implement these properties:
    class Page extends EventDispatcher{
        ...
        [Bindable]
        public function get from():int{
            return number * size + 1;
        }
        [Bindable]
        public function get to():int{
            return (number + 1) * size;
        }
        ...
    }
Notice the get syntax: this in ActionScript turns the function into a getter for a property of the same name. Also notice that I made these properties bindable.
Next, I need the previous and next buttons. I choose to sandwich the label in between them:
    <Button label="Previous" click="currentPage.prev()"/>
    <Label text="{currentPage.from} to {currentPage.to}"/>
    <Button label="Next" click="currentPage.next()"/>
Clicking on Previous will call the prev method and clicking on Next will call the next method. Simple. Here is the code:
        public function next(){
            number++;
        }
        
        public function prev(){
            number--;
        }
Compiled it, but it doesn't work. Clicking on Previous and Next does no change the display on the label at all. Oh! We need to fire the events! To fire a property change event, you'd do something like:
                this.dispatchEvent(new PropertyChangeEvent(
                    "propertyChange", true, true, 
                    PropertyChangeEventKind.UPDATE,
                    propertyName, null, null, this));
In our case, we are going to fire the change events for both from and to, because both of those must change after you flip a page. So we fire the changes:
        private function fireChanges(){
            var toFire = ['from', 'to'];
            for (var i = 0; i < toFire.length; i++)
                this.dispatchEvent(new PropertyChangeEvent(
                    "propertyChange", true, true, 
                    PropertyChangeEventKind.UPDATE,
                    toFire[i], null, null, this));
        }
        public function next(){
            number++;
            fireChanges();
        }
        public function prev(){
            number--;
            fireChanges();
        }
Ok, but I would like to disable the Previous or Next button when we are at the begining or the end of the dataset. No problem! We create a couple more bindable properties: hasNext and hasPrev:
        [Bindable]
        public function get hasNext():Boolean{
            return to < total;
        }
        [Bindable]
        public function get hasPrev():Boolean{
            return number > 0;
        }
Bind the buttons' enabled property to them:
    <Button label="Previous" click="currentPage.prev()" 
        enabled="{currentPage.hasPrev}"/>
    <Label text="{currentPage.from} to {currentPage.to}"/>
    <Button label="Next" click="currentPage.next()"
        enabled="{currentPage.hasNext}"/>
Make sure to fire the change events for them too when you are flipping pages(add them to toFire in the implementation of fireChanges) and we are done. The code: ex3.mxmlPage.as and live demo for example 3.

The end result of this is that you have very well separated MVC code. There isn't any code whose sole purpose is to sync the display like you get with jQuery code, for example. The code to do this is very minimal and naturally readable.

All code examples can be found here on Github.

Btw, I should mention that, the code inside of the curly braces does not allow any arbitrary ActionScript. There is some amount of flexibility, but not a lot. You can read more here.
Posted by Toby about 1 year ago about actionscript, flex and programming (0 comments)

jQuery Style BDD

I have been warming up to the jQuery style of using method chaining. Then I thought: why are most unit test frameworks class based? There's no reason why we can't do it in the jQuery style...
Something like this:
  $.describe('Bowling')
    .beforeEach(function(){
      this.bowling = new Bowling();
    })
    .should('score 0 for gutter game', function(){
      for (var i = 0; i < 20; i++) this.bowling.hit(i);
      this.bowling.score.shouldEqual(0);
    })
    .should('accumulate score', function(){
      this.bowling.hit(20);
      this.bowling.hit(10);
      this.bowling.score.shouldEqual(30);
    });
I am liking this syntax. The full bowling example is here. I guess I'll call this jquery.bdd. I just started using git/github btw. This is one of my first projects on it. Fork me. Fork me please.
Posted by Toby about 1 year ago about bdd, javascript, jquery and programming (8 comments)

Being Dynamic in Statically Typed Languages

Finally! Looks like we are all starting to get along:


Posted by Toby about 1 year ago about programming (0 comments)

Programming Without Braces

I was looking at this snippet of Javascript I wrote
	if (minutes) return minutes.minutes();
	else if (hours) return hours.hours();
	else if (days) return days.days();
	else if (weeks) return weeks.weeks();
	else return 0;
and noticed a certain beauty in this if-then-else statement in the fact that it doesn't contain any braces. Removing the braces de-clutters this code and makes it look...Zen.
The idea of using no braces was flashed out by Alan C Francis in his Redefining Simple article. So maybe I was subconciously affected by his article. What does this tell us about the use of braces though? Braces are not so nice. begin and end keywords and square brackets aren't so nice either...
I would also add that not using indentation at all also helps the beauty of the snippet. So this
	if (minutes) 
            return minutes.minutes();
	else if (hours)
            return hours.hours();
	else if (days) 
            return days.days();
	else if (weeks) 
            return weeks.weeks();
	else 
            return 0;
does not look as good as the original above. In fact I would say this is approaching ugly, with the beginning if the lines jumping back and forth. So this tells us that indentation isn't necessarily the answer either.
Another thing I would say about the original snippet is that it looks almost like ML or LISP, which is probably why I like it.

Posted by Toby about 1 year ago about programming (0 comments)

goodday dot py

Looking at rails and the datejs library, I can't help but feel bummed that Python doesn't have an expressive date library like that. So I started one. I've put up my code on google code
Posted by Toby about 1 year ago about dates, programming and python (2 comments)