Coolnamehere

January 30, 2008

PHP’s extract and compact functions

Filed under: php, tidbit — coolnamehere @ 6:51 pm

I’ve been brushing up on my PHP basics lately. Why? Well, it never hurts to
revisit things you think you already know. There is a good chance you will
discover something you didn’t know after all. For example: this time I
learned about PHP’s extract and compact functions.

extract takes an associative array and creates local variables on the fly, named for the keys in the array and with the corresponding values matched up. compact is the corresponding function for taking a collection of variables and stuffing them into an associative array.

<?php

    $book = array(
        "title"     => "Dad's Own Cookbook",
        "author"    => "Bob Sloan",
    );

    extract($book);
    echo $title . " was written by " . $author . "\n";

    $first = "Brian";
    $last  = "Wisti";
    $keys  = array("first", "last");
    $my_name = compact($keys);
    print_r($my_name);
?>

Running this code:

$ php -f extract-compact.php
Dad's Own Cookbook was written by Bob Sloan
Array
(
    [first] => Brian
    [last] => Wisti
)

extract is the more immediately useful of the two for my purposes, because
it simplifies a common tactic I use for creating local variables based on
database lookups.

Instead of manually creating local variables, like this:

<?php

    # ...
    while ($row = mysql_fetch_array($result, MYSQL_ASSOC)) {
        $author = $row["author"];
        $title  = $row["title"];
        # ...
    }
?>

I can save myself a little effort with extract.

<?php

    # ...
    while ($row = mysql_fetch_array($result, MYSQL_ASSOC)) {
        extract($row);
        # ...
    }
?>

I realize that there may be an even easier way to do it, but just this will
make my life noticeably easier as long as I don’t abuse it. I would mainly
tuck a call like this off in a function and probably use it in conjunction with
a SQL query or something else where I knew exactly what names I would end up with.

Why didn’t I know about this before? Well, the manual approach was good enough.
And since what I had was good enough, I didn’t think of looking for a better
approach. Then again, finds like this are exactly why I do go back and review
what I thought I already knew.

January 12, 2008

SiteTemplate Hiccup

Filed under: ruby, sitetemplate, writers block — coolnamehere @ 10:49 am

I’m trying to work out the next part of my Site generation series. There’s two problems. Writing the code is more interesting than the blow-by-blow account of how the code was written. I suspect anybody reading this would be more interested in using the library I’m talking about rather than writing it. I don’t really know, though, since what traffic I get is coming read about my messing with the Google API in Python. I’m pretty sure this series is for my own amusement, so maybe I’ll work on the code and then write a nice post about how to use it. Well, it’s 3 am. I don’t need to decide right now.

January 8, 2008

REBOL 3 alpha released

Filed under: rebol — coolnamehere @ 10:16 pm

I just saw Petr Krenzelok’s giddy announcement on the REBOL mailing list that a public alpha of REBOL 3 is available today. I’ve been waiting to see this for a while and can’t even begin to describe how excited I am.

The official announcement is here. I just need to remember that it’s an alpha. Pieces aren’t going to work. Things will be strange. But it’s out there for the bold to play with.

Of course I gotta get some work done, but you can be sure I’ll be poking at this over the next couple of days.

January 7, 2008

PageTemplate for Site Generation Part 2

Filed under: pagetemplate, ruby, sitetemplate — coolnamehere @ 10:15 pm

I’ve got my code filtering
Markdown

and now I want to stuff that filtered content into an HTML page. I could just use
maruku#to_html_document, but I need the ability to add details like a title and
site-related links.

I could use a format similar to my Python blog
files
. I won’t really need
PageTemplate if I do that, though. Not for the content file, anyways. That’s okay, though. The Maruku filter
was more of a proof-of-concept, anyways. PageTemplate will be useful for fitting the generated content into an
actual template, though.

That means I’m starting over on my content files.

Given a content file that looks like this:

title: A Simple Page
--
This page intentionally left blank.

I want an object that makes the title available in some way (simple Hash style access is fine), and makes the
HTML-formatted content available. After a few minutes of fiddling and poking around, I end up with tests and
application code.

Article Test Code

#!/usr/local/bin/ruby

require 'test/unit'
require 'SiteTemplate'

class TC_Article < Test::Unit::TestCase
    def test_article_file()
        article_file = 'simple.txt'
        assert(article = Article.new(article_file))
        assert_equal(article_file, article.source_file)
        assert_equal('A Simple Page', article.metadata['title'])
        assert(article.content =~ %r{<p>This page intentionally left blank.</p>})
    end
end

The Application Code

#!/usr/local/bin/ruby

require 'rubygems'
require 'maruku'

# A single HTML page generated by a content file
#
# Content files usually look like this:
#
#    title: My Title
#    --
#    Article contents
class Article
    attr_reader :source_file, :metadata, :content
    def initialize(filename)
        @metadata = {}
        parse!(filename)
    end

    def parse!(filename)
        @source_file = filename
        content = ''
        in_content = false
        File.open(filename).each_line do |line|
            if in_content then
                content << line
            else
                if line =~ /^--$/ then
                    in_content = true
                    next
                end

                if line =~ /^(\w+?):\s*(.+)$/ then
                    key = $1
                    value = $2
                    @metadata[key] = value
                end
            end
        end

        @content = Maruku.new(content).to_html
    end
end

It’s a really simple, slow parser, but it works. I won’t try to optimize it before I’ve actually figured out
what it’s supposed to be doing.

The Template

The next target is stuffing this content into a template. That’s easy. Here’s the template:

simple.html Template File

<html>
    <head>
        <title>[%var title%]</title>
    </head>
    <body>
        <h1>[%var title%]</h1>
        [%var content%]
    </body>
</html>

I could assemble my page manually if I felt like it. As a matter of fact, let’s do that in one of the tests.

Manual Page Generation Test

require 'PageTemplate'

class TC_HTML_Page < Test::Unit::TestCase
    def test_manual_page_generation()
        article_file = 'simple.txt'
        template = PageTemplate.new()
        template.load('simple.html')
        article = Article.new(article_file)
        template['title'] = article.metadata['title']
        template['content'] = article.content
        assert_match(%r{<title>A Simple Page</title>}, template.output)
    end
end

Do I really want to manually apply even that little bit of code, though? No, I don’t.

Automatic Page Generation Test

class TC_HTML_Page < Test::Unit::TestCase
    def test_standard_page_generation
        article_file = 'simple.txt'
        template_file = 'simple.html'
        assert(html_page = HTML_Page.new(:article => article_file, :template => template_file))
        assert_match(%r{<title>A Simple Page</title>}, html_page.to_html)
        assert_match(%r{<p>This page}, html_page.to_html)
    end
end

Automatic Page Generation Code

require 'PageTemplate'

class HTML_Page
    def initialize(opts = {})
        @article = Article.new(opts[:article])
        @template = PageTemplate.new()
        @template.load(opts[:template])
    end

    def to_html()
        @template['title'] = @article.metadata['title']
        @template['content'] = @article.content
        return @template.output
    end
end

Saving a File

Okay, now I have article files with content and metadata being consumed, formatted, and handed off to
PageTemplate for wrapping into a pretty HTML page. The only thing remaining at this stage is to actually
write the file.

Test Writes

require 'fileutils'

class TC_HTML_Page < Test::Unit::TestCase

    def test_standard_page_generation
        article_file = 'simple.txt'
        template_file = 'simple.html'
        output_file   = 'test/out.simple.html'
        FileUtils::rm_rf(output_file)
        assert(html_page = HTML_Page.new(
            :article     => article_file,
            :template    => template_file,
            :o utput_file => output_file))
        assert_match(%r{<title>A Simple Page</title>}, html_page.to_html)
        assert_match(%r{<p>This page}, html_page.to_html)

        html_page.write_to_file
        assert(saved_html = File.open(output_file).read())
        assert_match(%r{<title>A Simple Page</title>}, saved_html)
        assert_match(%r{<p>This page}, saved_html)
        FileUtils::rm_rf(output_file)
    end
end

Code to Make the Writes Happen

Oh heck, just take the whole thing. This is what my SiteTemplate.rb file looks like right now.

#!/usr/local/bin/ruby

require 'rubygems'
require 'fileutils'
require 'maruku'
require 'PageTemplate'

# A single HTML page generated by a content file
#
# Content files usually look like this:
#
#    title: My Title
#    --
#    Article contents
class Article
    attr_reader :source_file, :metadata, :content
    def initialize(filename)
        @metadata = {}
        parse!(filename)
    end

    def parse!(filename)
        @source_file = filename
        content = ''
        in_content = false
        File.open(filename).each_line do |line|
            if in_content then
                content << line
            else
                if line =~ /^--$/ then
                    in_content = true
                    next
                end

                if line =~ /^(\w+?):\s*(.+)$/ then
                    key = $1
                    value = $2
                    @metadata[key] = value
                end
            end
        end

        @content = Maruku.new(content).to_html
    end
end

# Takes an Article and a PageTemplate and mushes them together
# Note: This version still assumes that needed metadata will
# be available. This is not a safe assumption.
class HTML_Page
    def initialize(opts = {})
        @article = Article.new(opts[:article])
        @template = PageTemplate.new()
        @template.load(opts[:template])
        @output_file = opts[:output_file]
    end

    def to_html()
        @template['title'] = @article.metadata['title']
        @template['content'] = @article.content
        return @template.output
    end

    def write_to_file()
        FileUtils::mkdir_p(File.dirname(@output_file))
        File.open(@output_file, 'w') { |f| f.print to_html }
    end
end

Wrapup

This stage is done. We’ve taken some article files that look a lot like my blog files and turned them into
fully-fleshed HTML files. They will fit into a PageTemplate that’s been defined by the site maintainer,
guaranteeing a standard look for the site.

My next post on this topic will deal with putting an HTML_Page into the context of a larger site.

January 6, 2008

PageTemplate for Site Generation

Filed under: pagetemplate, ruby — coolnamehere @ 3:25 pm

So I was looking at my Python Blogger client and I started thinking. First, I thought that I could probably do the same thing in Ruby. Of course, the fact that my question in the RubyForge forum for gdata-ruby suggests that I might have to do a little more work doing this in Ruby. Then again, maybe the package author has just been really busy. Goodness knows I have let PageTemplate site idle for years at a time even though I still haven’t actually stopped developing it.

That, of course, set me off on yet another thought. What if I tried to define my posts in a PageTemplate file and used filters to handle the dirty work? Well, that might be a little challenge. But what if I used this approach to generate a whole Web site? Okay, yeah. That may have come out of nowhere for you. The truth is that I love static site generation tools, from ZenWeb to WebMake. These tools appeal to me because coolnamehere is pretty much a static site and I love anything which can give that pile of pages a common format without making heavy server demands. Honestly, loading up PHP just so I can have a templated site seems like overkill.

Let’s see if I can build a site like coolnamehere with Ruby and PageTemplate. I plan to borrow heavily from ZenWeb, since there are a lot of things to like about the ZenSpider approach. I especially like
building a site from a collection of pages and a chain of filters. Hey, PageTemplate has filters thanks to Greg Millam. Why don’t I try using them?

Start Small

I am going to start small, by teaching SiteTemplate about Maruku.

It took me a bit of time to get that much done, because I needed to relearn how PageTemplate initializes. Note to self: don’t ever go a full year without using your own library.

The test is simple: create a template using the Maruku filter. Compare the output of that template
with the text minus PageTemplate directives and fed into Maruku. The test passes if they look alike,
or close enough.

#!/usr/local/bin/ruby

require 'rubygems'
require 'test/unit'
require 'sitetemplate'

class TC_MarukuFilter < Test::Unit::TestCase
   require 'maruku'

   def test_maruku_filter
       content = "This is a paragraph"

       # template_file contains the text "[%filter :maruku%]This is a paragraph[%end%]"
       template_file = "maruku.txt"
       maruku_doc = Maruku.new(content)
       pt = PageTemplate.new()
       pt.load(template_file)
       assert_equal(maruku_doc.to_html + "\n", pt.output,
           "Check if Maruku filter ran successfully")
   end
end

Then the code I needed to make that test pass:

#!/usr/local/bin/ruby
# Utility for generating a static site with PageTemplate

require 'rubygems'
require 'maruku'
require 'pagetemplate'

class PageTemplate
   class DefaultPreprocessor
       class << self
           def maruku(text)
               return Maruku.new(text).to_html
           end
       end
   end
end

I cut corners by adding the maruku filter method to PageTemplate’s DefaultPreprocessor. PageTemplate’s internals need a little work, since this isn’t the prettiest way a person might want to add filters. It works well, but it’s not pretty.

That works well enough. Next time I’ll try a template filter, which puts the Maruku output into a template file of my choosing. That way we get the standard look for pages.

January 5, 2008

Another Link

Filed under: link, ruby, windows — coolnamehere @ 2:31 am

This blog may well be the single best resource ever created for those of use who are learning how to automate Windows with Ruby.

January 3, 2008

Xah’s Elisp Tutorial

Filed under: elisp, link — coolnamehere @ 10:10 pm

I’d really love to master an editor. Any editor. Vim has been my weapon
of choice for years, but Emacs has always intrigued me.
It’s easier to use than Vim, but the Elisp language is the real draw. Vim’s configuration /
scripting language is awkward at best. Elisp is cryptically lispish, but at least it is
possible to break it down without wondering what the heck <sfile> is supposed to be.

So yeah. I’m mostly a Vim guy but I’d love to get more out of my Emacs sessions. With that in
mind, I’ll be taking a close look at Xah’s Emacs and
Elisp tutorials.

January 2, 2008

Adding Categories to the Python Blogger Client

Filed under: blogger, gdata, python — coolnamehere @ 9:25 pm

I’ve already used my Python Blogger
client
for a couple
of postings, and I’ve been pretty happy with it so far. It still desperately needs
tags, though. Actually, Blogger calls them "labels." Actually actually, the Atom API calls
them "categories." Well, whatever they are called it looks like they are pretty easy to add.

You already know that tags are defined in my config header, and are simply a comma-delimited list
like so:

tags: python,gdata,blogger

Here’s the new sendPost method:

def sendPost(self):
    """Log into Blogger and submit my already parsed post"""

    # Authenticate using ClientLogin
    blogger = service.GDataService(self.__account, self.__password)
    blogger.source = 'post-to-blog.py_v01.0'
    blogger.service = 'blogger'
    blogger.server = 'www.blogger.com'
    blogger.ProgrammaticLogin()

    # Get the blog ID
    query = service.Query()
    query.feed = '/feeds/default/blogs'
    feed = blogger.Get(query.ToUri())
    blog_id = feed.entry[0].GetSelfLink().href.split("/")[-1]

    # Create the entry to insert.
    entry = gdata.GDataEntry()
    entry.author.append(atom.Author(atom.Name(text=self.__author)))
    entry.title = atom.Title('xhtml', self.config['title'])
    entry.content = atom.Content(content_type='html', text=self.body)

    # Assemble labels, if any
    if 'tags' in self.config:
        tags = self.config['tags'].split(',')
        for tag in tags:
            category = atom.Category(term=tag, scheme="http://www.blogger.com/atom/ns#")
            entry.category.append(category)

    # Decide whether this is a draft.
    control = atom.Control()
    control.draft = atom.Draft(text='yes')
    entry.control = control

    # Submit it!
    blogger.Post(entry, '/feeds/' + blog_id + '/posts/default')

I hope this works. If it does work, then I am going to do a little refactoring
as time allows to make this mess a little cleaner. If it doesn’t work, then I
guess I’ll have to … you know … fix it.

Reinstalling JRuby

Filed under: java, jruby, ruby — coolnamehere @ 10:46 am

I’m still getting everything back together after the iMac upgrade fiasco.
JRuby, my favorite Ruby implementation, is still
missing. I think I’ll fix that now.

  • Grab the binary of 1.03 from the JRuby
  • cd /usr/local
  • sudo tar xfvz ~/jruby-bin-1.0.3.tar.gz
  • sudo ln -s /usr/local/jruby-1.0.3/ /usr/local/jruby
  • Add JRuby details to my (somewhat busy) ~/.bash_profile

vim ~/.bash_profile

# I'm putting environment variables here and everything else in .bashrc
# echo "Processing bash profile ..."

# Custom install locations
export JRUBY_HOME="/usr/local/jruby"
# ...

# OS X is normally conservative about paths, while I am generous about them.
export LOCALBINS=/usr/local/bin:/opt/local/bin:/opt/local/sbin:/usr/X11R6/bin
export APPBINS=$JRUBY_HOME/bin
export PATH=$APPBINS:$LOCALBINS:$PATH

if [ -r ~/.bashrc ]; then
    . ~/.bashrc
fi

Source the file and test my path …

/usr/local brian$ . ~/.bash_profile
/usr/local brian$ which jruby
/usr/local/jruby/bin/jruby
/usr/local brian$ which gem
/usr/local/jruby/bin/gem

Test with the sample code from Getting Started
on the JRuby wiki.

require "java"

include_class "java.util.TreeSet"

puts "Hello from JRuby"
set = TreeSet.new()
set.add( "foo" )
set.add( "Bar" )
set.add( "baz" )
set.each { |v| puts "value: #{v}" }

Run it.

~/src brian$ jruby call_java.rb

Wait a very long time (why does Java startup have to be so slow on our Mac and how
the heck can I make it faster? It’s one thing that’s significantly worse on our Mac
compared to my PC). Eventually see:

:~/src brian$ jruby call_java.rb
Hello from JRuby
value: Bar
value: baz
value: foo

Excellent, it worked. It’s 2:46 now. I better post this and go to bed.

Blog at WordPress.com.