Wednesday, May 24, 2006

I will not recant!

Or, maybe I will. Kinda.

I miss the hell out of Zope3.

I admit, I never said I didn't love developing in Zope3, because that would have been a lie. Nor did I ever say Zope3 wasn't a better dev environment than RoR. That would have been untrue. What I did indicate was that RoR is a more streamlined web-centric development environment. and that's probably still true.

But today I was assigned a new development task. Build a watch-style solution for a piece of document management software. I'm excited about the project for a number of reasons, some technological, some ideological, and some of pure dorkery. But I immediately turned to zope for interfaces and for inspiration on implementing an observer pattern. I immediately went to zope for things that you can't find other places.

And that's when it hit me, afresh, though I had known it all along. Zope3 provides functionality at such a powerful level (interface/adapter oriented programming, object observers and event notification, etc) that only when I really need/want these things elsewhere do I realize how much their lack hurts.

RoR is good, sure. It provides a lot of shortcuts and time savers. But the further I get into core design with it, the more I realize that, while it's quite good and I've learned a good bit from it, I wouldn't want to design something massive in it.

I'm not declaring any kind of back-turning on RoR. I still very much appreciate how data-driven the design is. There are a lot of things that I think Zope3 can learn from RoR. But I think, long term, Zope3 has more to offer.

I would like zcml to do less. I would like more view-oriented helpers (maybe integrating python-webhelpers into the framework, along with Mochikit?). I'd like to see an ORM (sqlalchemy, if I had a choice) tied in as a utility that allows certain objects to register themselves with the framework, and (using zalchemy?) ties the transactions together. I would like to see a query language (xpath or oql) implemented.

But y'know what? All of that can come. Maybe I can spend some of my time on it. But I don't know if I think they're powerful enough complaints against the framework to make me want to use anything else. I'll keep at it with RoR, one because I have to, and two because it makes my thought process more diverse/adept/adaptable/etc. But I want to be back on Zope3.

Friday, May 19, 2006

design patterns

quick post... reading the head first design patterns book by o'reilly press. I'm only on page 17 and I'm already impressed. I had, at the outset, almost decided against liking this book. The "visual kitsch" is not an approach I would normally care for, and find, on the surface, mildly irritating. but having read their rationale for designing it this way, I can't really argue with them. also, I think it's actually proving effective, which I wouldn't have expected.

The first chapter's Duck example is excellent. The separation of unique and potentially variable behavior into a class in it's own right is a brilliant idea. I've actually done that in the past, on my own, but never really considered the what and why of it. The explanation and description of the pitfalls is excellent. That they lead you through the other potential ways of solving the problem, and show why they don't work, help crystialize the reasoning behind the desing solution.

I'm impressed.

I'll probably update this post as I go.

about.com and python

It's recently been said in the python community, most notably by Guido himself, that Python needs an evangelist. I agree with that. I feel the same way about Zope3 (despite some of my recent seeming frustrations). What keeps python from having the same buzz that Java generated back in the day? What keeps Zope3 from having the same buzz that RoR gets? I think, really, it's just evangelism. I think Zope3 has too steep a learning curve (speaking as someone who is really pretty comfortable with Zope3 all things considered), but that can be remedied.

So, evangelists. I grew up in a frighteningly charismatic christian church, so i've seen my share of real, honest-to-god evangelists. Usually they have shocking hair, boistrous voices, and a disturbing glaze to their eyes that indicates they are not entirely "there." So I don't want to be one of those. Nor do I want to be "The Python Evangelist", if for no other reason than it would be amazingly presumptuous. I'm just not the most qualified person for the job.

But, I would *love* to be "one of the python evangelists" (notice the difference in case sensitivity). I'd like to be one of the Zope3 evangelists too, but there are some things I need to figure out about where I stand on the platform for that to be realistic. It does a lot of things that I love, and it's architecture is better than any i've seen (and i've looked). To that end, I was made aware that About.com had listed for a Python guide. Last night I put together a quick "intro to python, with hello world" piece, and sent it off as my writing sample. I think it would be an interesting opportunity.

Assuming the likely case that other, more qualified people will apply for and get the Guide position, I've decided to post my writing sample here, just for kicks. Below is the writeup.


What is Python? At its most concise, Python is a programming language. It is an interactive, interpreted, dynamically typed, object-oriented language, with similarities to Perl, Ruby, and Java. But unless you know those languages already, or have a pretty good technology background, none of that will really mean much to you. So a better question might be "Why Python?"

Why Python? While there are plenty of potent technical reasons to suggest Python, the reasons that matter most to anyone as they start out with a new language usually fall into the following categories: simplicity of syntax, ease of portability, and library support for common tasks. Python's syntax makes it simple and enjoyable to pick up and use the language. This is important not only to keep you coding and enjoying the code you write, but is a serious consideration for maintainability and longevity of a program. Relatedly, the fact that there are implementations of Python for Windows, Linux/Unix, and MacOS make most Python code extremely portable. Finally, and most importantly as your skill with any language grows, the extensive core and third party libraries for Python mean that you are rarely forced to solve a problem "the hard way" (unless you want to, which Python certainly has the capacity to do), with support for everything from web, email, and database applications, to GUI and game programming.

That all sounds pretty good, but what about getting our hands dirty? No one ever learned anything worth knowing until they ended up doing it themselves. So let’s look at a little bit of code. If you don't have Python installed, there are a number of places to get it. Both the Python website and ActiveState have installers and instructions for Python, though if you're on a *nix box, the odds are you already have Python installed by the distribution. If not, there are downloads and instructions available for those platforms as well.

Once you have Python installed, let’s get to the interactive interpreter. In *nix you need only to type "python", and it will bring you to this shell. In Windows, you'll need to go to Start->Programs->Python->Python(command line). This should bring up a window with the Python version and build information at the top, and a line with >>> prompt below it.

Let's establish that we're in a friendly environment. Type "help" at the prompt. It should present you with a nice message on how to use the help command. Try it the first way, with no arguments. Type "help()". The parentheses tell the interpreter (the bit running in the background) that you want to call the command "help" with no arguments. It should present you with a few paragraphs letting you know some basic information about Python, and the help utility. Feel free to come back later, but for now, just type "quit".

Now it’s time to really type some code. Because "Hello World" is the most common first program to write in a new language, we'll only flout the convention a little by changing the phrase a bit. Type in the following:

>>> greeting = "hello from python!"

What did we do here? It seems simple, and, remarkably, it is simple. We merely created a variable named "greeting" and assigned a string to it by using the equal sign. A string is just any piece of text-like data, and "=" is just the assignment operator. It says "whatever is on the left hand side is now set, or assigned, to whatever value is on the right hand side." As you might expect, the value on the left hand side has to be a variable name. A variable is just an identifier that can have things assigned to it. We could just as easily have made "greeting" the variable "hi" or "good_day" or "helloWorld". (Purists will have to forgive me here. Technically, "greeting" is not a variable, but an object of type "string", with a __repr__ value that happens to look like string data, and the assignment operator helped us create the new object. But object theory would be getting a little ahead of ourselves, so we're going to keep saying variable for now).

So now we have a variable, and that variable has a value. While there are a nigh-limitless number of things we can do with a piece of text, let’s just, for now, print it out to the screen. Printing something out to the screen is as simple as using the "print" command. Just like we used the "help" command earlier, we will tell the interpreter that we intend to invoke the print command, but this time with an argument. At the prompt, type the following:

>>> print(greeting)

hello from python!

Notice that "hello from python!" showed up immediately below it? The "print" command told the interactive interpreter that you wanted to have the value assigned to the variable "greeting" echoed out to the screen. By placing parentheses around the variable in question, the command "print" knew that you intended that as the first and only argument.

There are a few things to know about the interactive interpreter to make life easier. The first is that any value typed at the prompt, if it is able to be meaningfully represented as a piece of text, will echo back out to the screen. Try it like this:

>>> greeting

'hello from python!'

Notice, however, a subtle difference between this output, and the output of the print command above. The output of the "print" command does not have quotes around it. It is, in fact, the direct result of "print". The second output is not actually the result of any specific Python command, but is rather a string representation of the data referenced by the variable "greeting". When you typed it directly, the interpreter showed you that it was, in fact, a piece of string data, by enclosing it in quotes. Pretty nifty. The other things to know about the interpreter are fairly straightforward, and are, in effect, just other ways of looking at what we've already seen. Try typing a mathematical expression at the prompt. Something exotic, like:

>>> 1+1

2

The interpreter isn't really doing anything spectacular here, though it is cool. The interactive interpreter knows that 1 is a numeric type. It also knows that when the plus sign "+" is used between two numeric types, what is expected of it is to add those values together in a numeric fashion, and present a result. To see a counter example, try this:

>>> "1" + "1"

'11'

Without getting into the more complicated reasons about why this is happening, by putting quotes around both "1"s, we've told the interpreter that we want these values to be treated like text. The plus sign in this case says to concatenate the values. Also notice that when we perform operations at the interactive prompt, any value that we don't assign back into a variable shows once, and disappears. While we may know that 1+1=2, the interpreter has no record of the value "2" anywhere. if we wanted that value for later use, we would have needed to assign it into some other variable, like so:

>>> sum = 1 + 1

>>> print(sum)

2

That's probably about enough for now. With what you've got you can already do some interesting things. Try out "help()" again, and look over what it says. At the "help>" prompt, type "str" (string, like the greeting we used) and look over some of the things you can do with strings. Try it with "int" too. Try to use other math operators (+, -, *, /) and see what happens. Try them on strings, see what happens when you multiply a letter by a number. I think you'll be pleasantly surprised.

Wednesday, May 10, 2006

Ruby, rails, and more thoughts on Zope3

the more time i spend with ruby on rails, the more convinced i am that, on some level, Zope3 has entirely forgotten that it's a web development platform.

i'm not badmouthing zope3. it has some of the smartest people working on it that i've ever seen in action. stephan, gary, phillip, jim (o' course), martjin, jeff shell, et. al. are brilliant bastards, certainly. no doubts about it.

but it's like zope3 has forgotten that it's a web development framework first and foremost. i say that in a way that is not to cast aspersions on zope3, but more to cast them on myself. i spent a year or more chugging away with zope3. and i learned a LOT about programming. but i learned really very little about real web development.

let me clarify.

i've spent more time messing with javascript, ajax, and css in three weeks with RoR than I ever TOUCHED with zope3. and that's my fault, certainly. but i didn't really have to, with zope3. i could spend my days whiling away on the controller level, once in a while messing with my interfaces and base classes at the model level, letting the view level just kinda sputter. not because i really wanted to, but there just wasn't that much effort or interest in view-level logic and interaction in the zope3 documentation and community.

that may be because they assume a level of mastery over that level for anyone who's coming to zope3 as a solution. but i'll be honest and say that isn't the case for me. i'm not a neophyte certainly, but i'm not an expert. i can write javascript when i have to, and i can throw a "this-was-designed-by-a-developer-wasn't-it" level bit of css together, but that's about it. ajax was a thing for other people, on other platforms... it wasn't that i didn't want to use it, but there was no standard way of integrating it, no out-of-the-box solutions for data access across ajax.

so why does it matter?

speed of implementation. rails says, even explicitly in the agile web dev book, that the visible web page is what it's all about. get it coded get it out, let your users see it. if you can't point and click at it, if you can't edit and create data quickly, sensibly, and immediately, something is wrong.

what rails gives you is the ability to do things fast, and it's smart enough behind the scenes to make sure that you have the flexibility to do them WELL without slowing you down.

zope3, you can and will do it well, very likely. but you're not going to do it fast. and anything special you want for your display, you're goin to do on your own.

one thing i love about rails is that data type validation is done at the model level. in zope3, up until VERY recently with the ability to put in validators in your interfaces, all of the type validation was done at the controller level. as far as i'm concerned, data validation is a model-level thing.

eh. i'm getting off on a tangent. but the point that i'm trying to make is still the same. zope3 has concerned itself so much with the soundness of its architecture, with the flexibility of it's model and controllers, that actually producing web pages has slipped to the background.

there are a lot of things i miss with zope3. interfaces and adapters are some of the most powerful programming constructs i've ever seen, and i daily miss them in rails. but as a tradeoff for javascript generation for standard ajax calls to data, the tightly integrated prototype and script.aculo.us libraries, the simple conventions, the EXCELLENT documentation...

i'll take RoR. and i'll hope for the day that zope3 finds its way here.

Wednesday, May 03, 2006

ruby adaption

i ran into a case last night where i think i'm trying to write python code in ruby.

only problem is, i like writing python code ;)

here's the gist of it:

lets say, for argument, that i have a standard User object. this user object is the most simple base class for, example, two other kinds of users, Admins and Customers. What I would do in python would be simply subclass Users and create the other two. now i admit, this gets a little tricky any time you're using an ORM. i can generally think of how i'd do it in sqlalchemy, just because i've used it more than the others, but still, it's not so "drop in and let it work" simple.

for ruby, and the rails orm, the only design pattern for subclassing requires a single-table design where all fields that will ever be needed by a subclass are contained in that same table. and while my "keep all subclass implementation independent!" hackles rise on hearing that kind of thing, in realistic situations, this is probably not a bad design choice. if the class is never going to be used outside of your own app, and esp in ruby/rails if the class is only to serve as the model component in MVC, subclassing it is only ever going to relate to your own data anyway.

but still, it bothers me.

my first thought toward a solution was having something like Users and CustomerDetails, and creating a non-table-tied mixin class Customer. doing something like Customer.new would create an object that had both User and CustomerDetail as parents, theoretically having the attributes of both. but that doesn't really work out with the ORM.

i'd heard ruby doesn't support multiple inheritance. i found this link by maurice codik who has an interesting solution for the problem, and it kinda works for what i want, but not entirely.

it seems like subclassing or multiple-inheriting from two model-dependent classes just doesn't really work, which makes sense. the orm doesn't have anything mapped for those attributes, so attempting a save will, at best, only save for the first parent that answers that method.

so, we override the save/update/edit/delete behaviors. this though makes me nervous. activerecord is a complex beast, and my ruby isn't good enough yet for me to feel comfortable knowing that i've overridden the behavior in all necessary places. i may revisit this, but not immediately.

also, it's not really a solution that i can reuse. every time i want to subclass something, i need to make sure that the two tables that the original classes are tied to don't have overlapping namespaces. and i'd need to catch every possible update, spin over both classes in the @parents array, check parent.kind_of?() and find out which one to append the new info to, then save them both.

it's possible, but it's not clean or pretty.

so my next thought was implementing a kind of simple adaption, one object to another. basically spin over all attributes and methods provided by one object, and append the relevant ones to a new instantiation of the kind of object i want to adapt to, and return the new style object back out.

this works, to a point. but the ORM again doesn't have any mapping to the new object, so i either maintain in the new object a list of parent-to-attribute mappings so that on all updates/creates/deletes etc i save back to the right kind of object, or... i don't know on the or. i couldn't think of any other way to do it.

but again, it's a poor solution. every time i want to save the hybrid object, i have to first look at, in this case, the User object, and pull up the CustomerDetail object with a find on the user id, and if one doesn't exist, create it, add it to the user object, and THEN translate my values back into it via some iteration over attributes.

again, none of this is impossible, but i don't know enough about ruby to really abstract it. so DRY goes right out the window.

hmm.