9 October 2008 4:23pm

Config-JFDI 0.04 should be on CPAN shortly

Config::JFDI mimics Catalyst::Plugin::ConfigLoader (without the need to load Catalyst). The new features in this release are:

  • Value substitution, just like C::P::CL (__HOME__, __path_to(...)__, __literal(...)__, etc.)
  • Ability to accept a default hash for a baseline configuration

I've just released Doc::Simply, a tool for generating documentation from JavaScript (or C, C++, etc.) comments.

I use it to for my b9j documentation

The idea came to me while developing some JavaScript: I was craving a way to drop in free-form documentation in a .js file as easily as I could in a .pm with perlpod.

The Doc::Simply format is modeled after Plain-Old-Documentation, but it is not an exact mimic. Body formatting, for example, is in Markdown (no C<>, L<>, ...)

However, if you are craving pure POD, then it shouldn't be too difficult to bolt a proper parser/generator on top of Doc::Simply

You can run it from the command-line as doc-simply

doc-simply < source.js > documentation.html

doc-simply --help

An example:

   /*
    * @head1 NAME
    *
    * Calculator - Add 2 + 2 and return the result
    *
    */

   // @head1 DESCRIPTION
   // @body Add 2 + 2 and return the result (which should be 4)

   /*
    * @head1 FUNCTIONS
    *
    * @head2 twoPlusTwo
    *
    * Add 2 and 2 **and** return 4
    *
    */

   function twoPlusTwo() {
       return 2 + 2; // Should return 4
   }

/*
 * @head1 AUTHOR
 *
 * Alice <alice@example.com>
 *
 * [http://example.com](http://example.com)
 *
 */


... which will generate this document

You can download it from http://search.cpan.org/CPAN/authors/id/R/RK/RKRIMEN/Doc-Simply-0.02.tar.gz

... or install it via cpan:

cpan -i Doc::Simply

11 September 2008 3:36pm

I've put together an interactive example of my JavaScript URI object:

b9j.uri.URI example

The example will also show the code necessary to perform the given operation (next to the result)

b9j.uri.URI overview:

  • Parses using Steven Levithan's parseUri 1.2
  • Easy path traversal (ascend to parent, descend to a child)
  • Set or add to the URI query (with proper encoding), get/interrogate the query (with proper decoding)
  • Add arbitrary data (unencoded) to the end of the query string, for extra flexibility
  • Manipulate/interrogate the scheme, user information (username & password), host, port, fragment, and more in a piecewise fashion
  • Add, change or strip the extension (html, js, css, ...) from a given URI

The code is available at http://appengine.bravo9.com/b9j/b9j.uri.js (19kb compressed / 5kb gzipped)

See also the documentation for b9j.uri

Screenshot of the interactive b9j.uri.URI example

Following my b9j.path release, I just finished up work on URI object-class for javascript.

It uses Steven Levithan's parseUri 1.2 to do the parsing.

Some example usage:

var uri = new b9j.uri.URI( "http://example.com/a/b?a=1&b=2&c=3&c=4&c=5" )
var host = uri.host()

var child = uri.child("c.html")            // http://example.com/a/b/c.html?a=1&b=2& ...
child.query().add({ c: [ 6, 7 ], d: 8 })   // ... ?a=1&b=2&c=3&c=4&c=5&c=6&c=7&d=8
child.query().set("b", 9)                  // ... ?a=1&b=9&c ...

return child.toString()

Again, while CPAN has URI.pm for URI-handling, JavaScript didn't really have an equivalent (parsing yes -- modifying/generating no).

b9j.uri - URI parsing, manipulation, and generation

b9j.path provides a way to parse, manipulate, and generate URI/UNIX-style paths.

CPAN provides lots of different options for path-handling and path-manipulation, but there didn't seem to be anything really available in JavaScript-land.

To fill the gap, I've released b9j.path:

 var path = new b9j.path.Path("/a/b/c")

 // /a/b/c/xyzzy
 var child = path.child("xyzzy")

 // /a/b
 var parent = path.parent()

 // ... and much, much, more...

It's part of my b9j javascript pack available at: http://appengine.bravo9.com/b9j/b9j-latest.zip

» Download

» Documentation

» Test

YUI Test is a great tool for javascript testing, but it has a couple of small issues:

  • Setting up the HTML document requires many different .js and .css assets
  • The actual javascript code to set up the test environment is not trivial (setting up the TestRunner, setting up the TestLogger, etc.)
  • A failed assertion will abort the test function immediately, preventing later tests from running

b9jTest addresses the problems above by:

  • Bundling all the required .js and .css assets into just two separate files
  • Providing some boilerplate javascript so you can jump right into testing without a lot of setup

An example b9jTest document:

<html>
<head>
    <meta http-equiv="content-type" content="text/html; charset=utf-8">
<title>b9jTest example</title>
<link rel="stylesheet" type="text/css"
    href="http://appengine.bravo9.com/b9j/b9jTest.css">
<script type="text/javascript"
    src="http://appengine.bravo9.com/b9j/b9jTest.js"></script>
</head>
<body class="yui-skin-sam">
<div id="testLogger"></div>
<script type="text/javascript">

    b9jTest(function(test) {
        test.areEqual("xyzzy", "xyzzy");
        test.areEqual("apple", "apple");
        test.areEqual("banana", "banana");
    });  

</script>
</body>

b9jTest() is a global function that accepts a function that is called with a testing object. The testing object provides access to the standard YUI assertions and more.

Documentation:

http://appengine.bravo9.com/b9j/documentation/b9jTest.html

Download:

b9jTest.js
b9jTest.css

Example of copying text into the clipboard

Firefox won't allow copying text into the clipboard via javascript for a variety of reasons.

For better or worse, you can get around this problem with a little .swf (Flash):

    function copyIntoClipboard(text) {

        var flashId = 'flashId-HKxmj5';

        /* Replace this with your clipboard.swf location */
        var clipboardSWF = 'http://appengine.bravo9.com/copy-into-clipboard/clipboard.swf';

        if(!document.getElementById(flashId)) {
            var div = document.createElement('div');
            div.id = flashId;
            document.body.appendChild(div);
        }
        document.getElementById(flashId).innerHTML = '';
        var content = '<embed src="' + 
            clipboardSWF +
            '" FlashVars="clipboard=' + encodeURIComponent(text) +
            '" width="0" height="0" type="application/x-shockwave-flash"></embed>';
        document.getElementById(flashId).innerHTML = content;
    }

The above trick will also work in Safari, IE, and Opera.

Download clipboard.swf

Alternatively, you can use the Google App Engine hosted version at
http://appengine.bravo9.com/copy-into-clipboard/clipboard.swf

(Originally adapted from http://www.jeffothy.com/weblog/clipboard-copy/)

Getopt::Chain

Option and subcommand processing in the style svn(1) and git(1)

Getopt::Chain will help you make your command-line scripts work like this:

    ./script --opt --opt=<value> cmd --opt ...
    ./script cmd ...

NOTE: Any option specification covered by Getopt::Long is fair game.

Here is some example usage:

    #!/usr/bin/perl -w

    use strict;
    use Getopt::Chain;

    # A partial, pseudo-reimplementation of git(1):

    Getopt::Chain->process(

        options => [qw/ version bare git-dir=s /]

        run => sub {
            my $context = shift;
            my @arguments = @_; # Remaining, unparsed arguments

            # ... do stuff before any subcommand ...

        }

        commands => {

            init => {
                options => [qw/ quiet|q --template=s /]
                run => sub {
                    my $context = shift;
                    my @arguments = @_; # Again, remaining unparsed arguments 

                    # ... do init stuff ...
                }
            }

            commit => {
                options => [qw/ all|a message|m=s /]
                run => sub {
                    my $context = shift;

                    # ... do commit stuff ..
                }
            }

            add => ...

            help => ...

            ...
        }
    )

    # The above will allow the following (example) usage:
    #
    # ./script --version
    # ./script --git-dir path/to/repository init
    # ./script --git-dir path/to/repository commit -a --message '...'
    # ./script commit -m '~'

The source repository is on GitHub: http://github.com/robertkrimen/getopt-chain/tree/master

16 August 2008 9:12am

Hash::Param

A CGI/Catalyst::Request-like parameter-hash accessor/mutator

    my $params = Hash::Param->new(parameters => {
        qw/a 1 b 2 c 3/,
        d => [qw/4 5 6 7/],
    })

    $result = $params->param( a )           # Returns 1
    $result = $params->param( d )           # Returns 4
    @result = $params->param( d )           # Returns 4, 5, 6, 7
    @result = $params->params               # Returns a, b, c, d
    $result = $params->params               # Returns { a => , b => 2,
                                                        c => 3, d => [ 4, 5, 6, 7 ] }
    @result = $params->params( a, b, d )    # Returns 1, 2, [ 4, 5, 6, 7 ]
    %result = $params->slice( a, b )        # Returns a => 1, b => 2

              $params->param( a => 8 )      # Sets a to 8
              $params->param( a => 8, 9 )   # Sets a to [ 8, 9 ]

The source repository is on GitHub: http://github.com/robertkrimen/hash-param/tree/master

13 August 2008 10:49pm

String::Comments::Extract

    my $comments = String::Comments::Extract::C->extract($source)

String::Comments::Extract is a tool for extracting comments from C/C++/JavaScript/Java. The extractor is implemented using an actual tokenizer (written in C via XS [adapted from JavaScript::Minifier::XS]). By using a tokenizer, it can correctly deal with notoriously problematic cases, such as comment-like structures embedded in strings:

    std::cout << "This is not a // real C++ comment " << std::endl
    printf("/* This is not a real C comment */\n");
    # The extractor will ignore both of the above

The source repository is on GitHub: http://github.com/robertkrimen/string-comments-extract/tree/master