Dissing Programming Langauges...

Posted by ben Wed, 03 Aug 2005 06:40:14 GMT

A friend recently brought to my attention that I seem to have a knack for insulting a language based on a quick overview without actually getting into it. After thinking about this awhile I’ve noticed it definitely seems to be more of a trend than I’d like.

The latest example of this was Ruby, but before that Python (Lisp before that). With each language, I’d find new things to nitpick on before really diving in (Which I always seemed to do anyways). As a result of this, I think its time for me to really grow up and stop picking on new languages till I’ve at least had the time to write a few apps in them and truly understand where they’re lacking.

As a quick side-note, here are the things I found to nitpick on before actually learning these languages:

  • Objective-C: God, wtf is up with these [] all over the place?
  • Lisp: God, wtf is up with these () all over the place?
  • Python: God, wtf is up with these _ all over the place, and who in their right mind uses white-space to designate blocks?
  • Ruby: God, wtf is up with these {|| } things all over the place?

Obviously I had issues with syntax, I hope to have purged myself of these issues as of this writing. Remember, sometimes a language has a damn good reason to have crazy characters all over the place.

The Myghty Python Web Framework 1

Posted by ben Wed, 06 Jul 2005 03:13:20 GMT

Myghty is a powerful web application framework that builds on the strengths of Perl’s Mason using my favorite language, Python. While Myghty also handles page templating, it has some very powerful features that make it a full fledged framework in my opinion.

Wondering if Myghty is something you should check out?

I would highly recommend looking into Myghty if you’re looking for any of the following:

  • Clean, unobtrusive templating language for integrating Python data and HTML
  • An easy way to write pages that inherit their look/feel without needing .include statements
  • Re-usable code that you can carry from one project to the next
  • MVC programming paradigm , or page-driven paradigm (Myghty doesn’t care, it can be anywhere in between as well)
  • Advanced caching ability, tunable to just the time intensive sections of a page
  • Run as WSGI, FCGI, SCGI, mod_python, or stand-alone with hardly a single change to your code (to switch between them)
  • Architecture that scales, from a single developer’s personal project, to a multi-developer’s company website

History

Myghty first came into existence a little over a year ago when Mike Bayer looked around at the Python web application frameworks and couldn’t find one that really sucked him in. Having used Mason for various projects, but not finding anything as elegant as it for Python he decided to port Mason to Python.

This ported code was based on the Perl code almost line by line, resulting in a Python web application framework that worked almost exactly like Mason, except with Python as the base language. It wasn’t very long before Mike started adding to Myghty, such as Module Components, and threaded features not found in Mason.

Terms Used

MVC – Model, View, Controller paradigm is typically used to describe an approach that separates the code in these 3 steps rather than mingle them all in one place. Model usually refers to the database and its abstraction, the Controller handles the logic of dealing with the request, getting the data from the Model, and passing it to the View for display to the user.

Component – A component in Mason is what a template is known as. A template might display a full web-page, just one small section, or send data to other components. Components typically correspond to files on a filesystem, and appear as normal HTML pages with a few lines of Python in them, although they’re far from normal HTML pages…

Resolver – Used to describe the process of determining what to do with a given URL. Myghty has some incredible flexibility here.

Distinctive Features

Module Components

In Python, your modules contain all your Python code. In Myghty, the Component contains a mixture of Python with HTML. To handle a MVC approach to development, Myghty has Module Components. These are a cross of a Myghty Component with a Python Module, that leaves you with a fairly normal Python Module. It’s called directly from Myghty in a similar way to how Myghty would call a Component. These Module Components can be explicitly defined in the configuration, or called implicitly.

The Module Component allows you to directly handle URL’s, and respond from within the Module by sending data back to the client or processing data then sending it on to a normal Component for processing. Module Components work within the Resolving process, and have control passed to them according to a configuration mapping . The mapping uses regular expressions to match URL’s to modules that contain the method to be called, like so:

module_components = [
    {r'myapplication/home/.*' : 'myapp.home:HomeHandler'}, 
    {r'myapplication/login/.*' : 'myapp.login:LoginHandler'}, 
    {r'.*/cart' : 'myapp.cart:process_cart'}, 
    {r'.*' : 'myapp.home:HomeHandler'} 
]

This would map the URL http://yourapp/myapplication/home/blah to the HomeHandler component class, in the home module file, under the myapp package. The method in the HomeHandler class called can be done in two different ways, implicitly, or explicitly. For more details on this, read the Myghty docs on Module Components

While other frameworks implement an MVC approach, Myghty’s goes a step farther by letting you tie it into the resolution options used by the Resolver, and even use your own resolvers…

Advanced Resolver

In version 0.97alpha2 (maybe a better naming scheme could be used? :) the resolver implementation was heavily rewritten to allow massive flexibility in how a URL is handled. When Myghty handles a request, it goes through a fairly complex process to determine what to do with it. This is called the default strategy:

  1. Translate the URL against the path translation options – This can act as an internal URL rewriting scheme, changing a URL to be handled differently
  2. Resolve a dhandler – This rule matches conditionally. It is only called if the rules under it don’t find a match, it then strips off part of the URL its looking for and adds dhandler. So a search for /article/view/38 becomes a search for /article/view/dhandler if 38 isn’t found. Read more about dhandlers
  3. Check the URI Cache – The URI cache is searched for the incoming URI at this point, to see if a component has matched it before. This rule is also executed on a conditional basis.
  4. Upwards Search – The 3rd conditional rule of the bunch, this rule is called during inheritance when a component searches parent paths for an autohandler. Read more about autohandlers
  5. Resolve a Module – Matches a Module Component to the incoming URI.
  6. Resolve a File – Matches a file under the component roots specified for a match to the incoming URI. This will directly call a Myghty template file on the system in response to a URL.

As soon as any of the non-conditional rules matches, the file or module component method is served up. Myghty gets an additional speed boost by keeping compiled versions of all your template files around, so that they’re only compiled the first time they’re called. By using the basic Python way of compiling the templates as .pyc files, it also ensures that they’re re-compiled the second you update one of them. This way you’re always sure you’re viewing the latest update of your template.

So where’s the real power in all this?

These are just the default rules. They can be completely, and utterly customized. You can switch the order around, you can remove resolver rules entirely, and you can even write your very own resolver rules.

Maybe you’ve decided you want to serve some of the components from a database instead of the file system. You could write a custom resolver rule that does just that, and loads the entire component from the database as well. You could write your own version of the Path Translate module to get its rewrite rules from a database table. As if this wasn’t powerful enough, Myghty also lets you have different resolver strategies for different contexts.

Conclusion

Myghty is a very powerful web framework. It has features that go beyond any pure “templating” language I’ve encountered. If you’re not quite happy with what you’re using right now, or something I described above makes you giddy with thoughts of power, maybe its time to take Myghty for a test drive.

In future posts, I’ll describe more of the features I use daily in Myghty, and share code tidbits that let you get complex tasks done easily.

Sending RSS items to Growl via AppleScript / NetNewsWire 2

Posted by ben Sun, 02 Jan 2005 18:59:33 GMT

A friend alerted me the other day to some useful sites for tracking packages via RSS. There’s one for FedEx and one for UPS.

While these are quite cool, and nice to have in NetNewsWire, the same friend told me its even cooler to have updates to the feeds sent to Growl. That way you get a nice little pop-up window when one of your packages has moved on.

He wrote a Python script that sent RSS updates over the LAN to Growl (Note: The Python script requires netgrowl.py). While that was cool, his method of adding feeds to watch (editing a text file on a remote machine) didn’t seem that cool. It was about that time that I started thinking, well, why not have Net News Wire manage the feeds? That is what its for after all… so I started looking to see how I could tie this togther.

Searching through ranchero.com quickly led me to a page on calling scripts as subscriptions. So I figure, if I write an AppleScript, that searches a NetNewsWire (NNW) folder for unread items then sends them to Growl via the Applescript interface, that should do the trick.

So without further ado, here’s my little Applescript, based on Brent Simmons Folder Watch example, that searches a NNW folder for unread items, sends them to Growl, then marks them as read.

Installing

GrowlRSS.scpt:

Download this where desired, add it as a script in NNW using the File menu -> New Special Subscription -> Script. Then locate the GrowlRSS script, it will then open the Show Info window. Expand the Script Settings option, and put the name of the NNW folder to watch as the Args. Close, and hit refresh, and it should send any unread items in that folder to Growl.

Caveat

You should have Growl 0.6 from svn installed. It’s possible it’ll work with 0.5, but I haven’t tested that.

Fast CGI with HTML::Mason 1

Posted by ben Sat, 18 Dec 2004 18:55:04 GMT

I decided the other day for a variety of reasons, to switch from the mod_perl web-app style to using Fast CGI with HTML::Mason. I only found one application out there that uses Mason with Fast CGI, which was the RT Ticket tracking system. However, the way it was setup didn’t match my requirements, nor the way their handler was configured.

After scouring the Internet some more, I hobbled together a working config section for my Apache+mod_fastcgi along with a template for the mason handler. Since I haven’t seen anything going over these basic steps anywhere else, here’s how I did it.

First, the things you should have installed on your system (which I’m assuming is nix):

  • HTML::Mason
  • CGI::Fast
  • Apache w/mod_fastcgi

Fast CGI runs your CGI application for you, and handles connections between your application, and the webserver. To configure your application, you just use one of the Fast CGI development kits for the response event loop within which your application will process requests.

Since we’re using Perl, there’s already a handy module available from CPAN that I chose to use called CGI::Fast. Then we just wrap our Mason call within a CGI::Fast loop to handle requests. This Perl script will be kept running persistently so that you get the advantage of not having to startup the Perl interpreter on every web page request.

Why Fast CGI instead of mod_perl?

  • You can use the connection feature of Fast CGI to run your app on a different machine than the webserver
  • Your application takes up only as much ram as Perl + your modules
  • It’s more portable, you don’t even need to use Apache, any webserver supporting Fast CGI will work
  • Less configuration files to maintain

Before showing my standard handler template, you should be aware of how I organize a Mason site using Fast CGI. Here’s what my directory structure looks like under the fictional GroovieWebapp:

  • GroovieWebapp/
    • site/
    • modules/
    • libs/
    • mdata/
    • mason-handler.fcgi

Site is where all of our Mason code (and images, css, etc.) is going to go. Modules is for putting any of your own Perl modules into that you made specifically for this webapp. libs for Mason libs, mdata for your Mason data dir, and finally the handler itself. This structure puts your whole webapp under one directory which is also useful under version control systems like Subversion.

Here’s what my standard handler template looks like (mason-handler.fcgi):

#!/usr/bin/perl
use FindBin qw($Bin);
use lib "$Bin/modules";

use strict;
use HTML::Mason::CGIHandler;
use MIME::Lite;  # Need this to e-mail the error

# Enter CGI::Fast mode, which should also work as a vanilla CGI script.
require CGI::Fast;

my $h;
my $show_error = 1;  # Should we e-mail the error, or dump to the web?

if ($show_error) {
    $h = HTML::Mason::CGIHandler->new
        ( comp_root => [
              [main => "/usr/local/www/GroovieWebapp/site"],
              [libs => "/usr/local/www/GroovieWebapp/libs"]
          ],
          data_dir => "/usr/local/www/GroovieWebapp/mdata",
          allow_globals => [qw($Schema $Session)]
        );
} else {
    $h = HTML::Mason::CGIHandler->new
        ( comp_root => [
              [main => "/usr/local/www/GroovieWebapp/site"],
              [libs => "/usr/local/www/GroovieWebapp/libs"]
          ],
          data_dir => "/usr/local/www/GroovieWebapp/mdata",
          error_format => 'text',
          error_mode => 'fatal',
          allow_globals => [qw($Schema $Session)]
        );
}

# Setup our global vars for the request, preload our modules
{ package HTML::Mason::Commands;
  use Apache::Session::File ();    # My favorite for session tracking
  use CGI::Cookie;                 # Also for session tracking
  use DBI;   # It's nice to load and establish our db connection in advance
  DBI->install_driver("mysql");  # Put in whatever connection you use, etc.

  # This allows our $Schema variable to be accessed from anywhere in our Mason site
  our $Schema = DBI->connect_cached("dbi:mysql:DB_NAME","DB_USER", "DB_PASSWORD" );
}

# These two subroutines add Mason functionality that was lost by using CGIHandler instead
# of the ApacheHandler.
sub HTML::Mason::FakeApache::document_root {
    my $self = shift;
    return $ENV{'DOCUMENT_ROOT'};
}
sub HTML::Mason::FakeApache::param {
    my $self = shift;
    my $key = shift;
    my %args = HTML::Mason::Utils::cgi_request_args($self->query,$self->query->request_method);
    my @keys = (keys %args);

    if ($key) {
        return $args{$key};
    } else {
        return @keys;
    }
}

# For e-mailing the error
sub send_email {
    my $error = shift;
    my $cgi = shift;
    my $site = $cgi->virtual_host();
    my $time = localtime;
    my $body = "----------------------------------------\n";
    $body   .= "-------- ERROR OCCURRED\n";
    $body   .= "----------------------------------------\n";
    $body   .= "\n\n";
    $body   .= "WEBSITE INFO\n";
    $body   .= "Site: $site\n";
    $body   .= "Full URI Request: ".$ENV{'REQUEST_URI'}."\n";
    $body   .= "Time: $time\n";
    $body   .= "\n\n";
    $body   .= "USER INFO\n";
    $body   .= "Referrer: ".$cgi->referer()."\n";
    $body   .= "User-agent: ".$cgi->user_agent()."\n";
    $body   .= "IP Address: ".$cgi->remote_host()."\n";
    $body   .= "Cookie info: ".$cgi->raw_cookie()."\n";
    $body   .= "\n\n";
    $body   .= "MASON ERROR\n";
    $body   .= $error;

    my $msg = MIME::Lite->new( From    => 'YOUR SITE <WHERE@WHEREVER.COM>',
                               To      => 'YOUR_EMAIL_ADDRESS',
                               Subject => "$site Mason Website Error occured at $time",
                               Data    => $body,
                             );
    $msg->send('smtp','YOUR_SMTP_SERVER');
}

while (my $cgi = new CGI::Fast) {
    # the whole point of fastcgi requires the env to get reset here..
    # So we must squash it again (This was copied from RT's Fast CGI handler)
    $ENV{'PATH'}   = '/bin:/usr/bin';
    $ENV{'CDPATH'} = '' if defined $ENV{'CDPATH'};
    $ENV{'SHELL'}  = '/bin/sh' if defined $ENV{'SHELL'};
    $ENV{'ENV'}    = '' if defined $ENV{'ENV'};
    $ENV{'IFS'}    = '' if defined $ENV{'IFS'};

    # The Mason $r->uri includes the script name unless we do this
    $ENV{'SCRIPT_NAME'} = '';

    # This deals with someone entering yoursite.com/ to make sure it serves index.html
    if ( ( !$h->interp->comp_exists( $cgi->path_info ) )
        && ( $h->interp->comp_exists( $cgi->path_info . "index.html" ) ) ) {
        $cgi->path_info( $cgi->path_info . "index.html" );
    }
    #$cgi->header(-charset=>'utf-8');  # If your site uses utf-8 characters

    # Ping our db handle
    $HTML::Mason::Commands::Schema->ping;

    # Lets try and handle this...
    eval {
        $h->handle_cgi_object($cgi);
    };

    # Something happened, if it couldn't find the component, don't fatal
    # we'll re-process as a 404
    if ($@) {
        if ($@ =~ /could not find component/) {
            # If you want Mason to handle the error so you can keep your site appearence,
            # this will call Mason to display your error page. If the error was in the
            # code that is called on every page, this will cause trouble.
            $cgi->path_info('/errordocs/404.html');
            $h->handle_cgi_object($cgi);
        } else {
            send_email($@,$cgi);
        }
    }
}

This template has the option of either showing the Mason debugging output to the screen, or capturing the error, displaying a pretty 404 of your desire, and e-mailing you the error that occured.

Now for the Apache httpd.conf:

# Tell FastCGI to put its temporary files somewhere sane.
FastCgiIpcDir /tmp

# Increases processes if you have heavy loads to deal with
FastCgiServer /usr/local/www/GroovieWebapp/mason-handler.fcgi -idle-timeout 120 -processes 2 

<VirtualHost *:80>
    ServerAdmin webmaster@YOUR_SERVER
    ServerName YOUR_SERVER_NAME

    AddHandler fastcgi-script fcgi
    ScriptAliasMatch (.*\.html$) /usr/local/www/GroovieWebapp/mason-handler.fcgi$1
    ScriptAliasMatch (.*\/$) /usr/local/www/GroovieWebapp/mason-handler.fcgi$1

    DocumentRoot /usr/local/www/GroovieWebapp
</VirtualHost>

Restart apache, and check apache’s error_log to see that Fast CGI started up your application properly. Obviously, tailor these samples as needed. Another useful trick when running your webapp this way, is that if you kill the perl processes the apache Fast CGI process manager starts for you, it’ll re-spawn them. I find this a quick way to re-load individual webapps if I made changes to a module they’re using.

Enjoy!*

A Mason component to fill-in error messages

Posted by ben Fri, 26 Nov 2004 18:53:31 GMT

How often have you seen those sites with a form, and after you miss a field, it pops up a little message in red under the text box indicating what went wrong? I’m guessing a lot, and when writing web applications this can be a pain to code in.

Typically in Perl/PHP, you’d add this by going through your form, adding little bits of code under each and every form element to display the error on invalid input. I quickly got tired of this, and decided to simplify the process.

In examining HTML::FillInForm, I noticed I needed a slightly different tool to easily parse the HTML form. After a little bit of shopping about, I decided on HTML::Parser to do the heavy lifting. Here’s a snippet from a Mason page using my error_fill.mas component to toss in the error messages:

% if ($form_status eq 'errors') {
<&| /lib/error_fill.mas, errors => $errors_hash_ref &>
<p style="color: red; font-weight:bold;">Errors Found: Please correct the fields indicated.
<& /forms/myform.mas, %ARGS &>
</&>
% } else {
<& /forms/myform.mas, %ARGS &>
% }

$errors_hash_ref is a hash ref that is keyed by the form field name, with the value set to the error message you want displayed. Right now, error_fill.mas does not have custom pre/post-fix variables for you to tweak how the error message is inserted. It currently puts a <br> followed by a red span element containing your message.

myform.mas is the form I want to display, I prefer to split sections out in Mason to keep my web application modular and the sections of my pages more reusable.

Here’s error_fill.mas:

<%args>
$errors
</%args>
<%init>
# $errors is a hash reference keyed as:
#     'field name' > 'Error message'
use HTML::Parser;
my $form = $m->content;

my $p = HTML::Parser->new(api_version > 3);
$p->handler(default => sub { print @_ }, "text");
$p->handler(end => sub {
              my ($tagname, $text, $attr) = @_;
              if (($tagname eq 'select') && (exists $errors->{$attr->{name}})) {
                print $text;
                print '<span style="color:red;">';
                foreach my $err (@{$errors->{$attr->{name}}}) {
                  print '<br />'.$err;
                }
                print '</span>';

              } else {
                print $text;
              }
            },
            "tagname, text, attr");
$p->handler(start => sub {
              my ($tagname, $text, $attr) = @_;
              if (($tagname eq 'input') && (exists $errors->{$attr->{name}})) {
                print $text;
                print '<span style="color:red;">';
                foreach my $err (@{$errors->{$attr->{name}}}) {
                  print '<br />'.$err;
                }
                print '</span>';

              } else {
                print $text;
              }
            },
            "tagname, text, attr");
$p->parse($form);
</%init>

Hopefully this will help some people out.

Older posts: 1 ... 5 6 7 8