Apr. 13th, 2009

NorWesCon

Apr. 13th, 2009 09:10 am
elfs: (Default)
Other than getting Kouryou-chan's money stolen, the con was pretty good. Attended a few panels, had one drop out on me because nobody else showed up, and enjoyed the masquerade a lot.

It felt... older than it has in the past. Fewer young people. Lot of greybeards. I don't know if that's economy or we're already living in the most likely future, and the kids already know it.

The panel on e-publishing was kinda a wash. It mostly came down to "Nobody knows what's going to be happening in the future, but we'd really like everyone to settle on one standard for e-books." Whee. The same wash was true for "writing a novel in a year." Overwhelmingly, I've been made aware that the problem, my problem, is that I get "ooh, shiny!" from a new story idea and abandon the old one. I must conquer my da Vinci Syndrome or I'll never get anything done. Every one of the people on that panel said "Finish the rough draft. It'll be crap. But it'll be done crap."

The girls alternated between being thrilled, and being bored. Yamaraashi-chan, especially, is champing at the bit to be 13 and allowed to go to panels on her own, with her own friends.

All in all, a good time. Very little money was spent; we actually brought lunches from home. We got back home Sunday afternoon and napped for an hour, and still managed to sleep soundly when it was bedtime.
elfs: (Default)
The other day, as I was looking through an acquaintance's library, I came across the Open Gaming License book, The Book of Erotic Fantasy, which is intended as a rule add-on to Dungeons & Dragons, D20 edition. There was a bit of a kerfluffle after the book started circulating and Wizards of the Coast amended the rules of their public license to prevent works from circulating that were obscene, pornographic, or contrary to community decency.

They needn't have bothered. The book isn't really worthy of the attention.

There are two sections of the book worth attention: the spells section and the monsters section. The monsters are interesting and, if a bit predictable, still contain a lot of sparks for interesting campaigns. You'll have to work to create the scenarios suggested for many of the monsters, but they're still worth it. The spells are clever and intriguing.

On the other hand, the characters, feats and skills section of the book suggest a complete paucity of creativity. The three standard classes offered are "Imagist" (an illusionist who works primarily with beauty), "Kundalist," (a kind of sex monk), and "Tantrist," (a mage who uses sex to raise power). The whole theif/bard/rogue end of the business, with prostitutes, courteasans, and so forth is ignored, and if you're gonna run a city campaign those would be great roles to play. The feats and skills sections are weak, and suggest an unfortunate tendency to try and impress modern "altie" sexuality on the Dungeons & Dragons world. Piercing and tattooing were not always sexual, and were not always associated with alternative sexuality, but they are in this book.

There's an okay section in the beginning in which the writers try to be adult about the whole thing. For the most part they succeed, but they were preaching to a critical member of the choir when I read it. I couldn't help but hear the sniggering in the background.

It probably doesn't help that the illustrations are, for the most part, photographs, many of them digitally edited for special effects, and many of them straight-up nudes. Not the sort of book you can read in public.

All in all, this book isn't a great addition to either the D&D collection of books, or to the further understanding of human sexuality. Most of the rules are the sorts of things a good gamemaster could come up with on their own, the creative effort is somewhat pauce, and the sexuality much more modern than is appropriate. I think the book succeeds mostly in its final page, where Phil Foglio pretty much makes the same case that I did: good players and game masters will handle sex the way they handle any strange encounter, and the existing rules are sufficient to the game. This book was not required, and it does little to further the genre.
elfs: (Default)

As a web developer, I tend to have a lot of projects just hanging around on my hard drive, waiting for me to pay attention to them. And one of the things that becomes onerous over time, especially if you’re like me and a bit undisciplined about the whole folders, projects, and so on thing of keeping stuff in the right places, is keeping track of where all of them are.

Most of my projects are individual web emplacements: wordpress designs, or extensions of the narrator software package I wrote and have been using to deploy my stories since 1994 or so. (It’s been through three languages in that time– started as Perl, was re-written as Python, briefly converted to Ruby, and now is back to Python again.) But how to keep track of them all?

I decided to experiment with Nginx, a popular and powerful reverse proxy engine. A reverse proxy accepts connections from the outside world as if it were your head webserver, and then forwards those requests to the appropriate server. Nginx (pronounced “Engine X”) is also capable of acting as a high-performance webserver in its own right, so it can serve media files out, and it uses a PCRE engine with variable substitution and a limited conditional language allowing you to make content delivery decisions based on header information long before you invoke the machinery of a heavy back-end like PHP, Django, or Rails.

I would call the experiment a modest success. My idea is eventually to have the ElfSternberg.com website front-ended with nginx and running a number of different back-ends, such as the wordpress install with Apache, other dynamic content via Django or Rails, and static content via Nginx itself.

One thing I did after installing Nginx on my dev box was to write a little CGI script that looked at the configuration file and generated a local index of projects. My Nginx configuration file is reasonably regular, and looks like this with one proxy_pass command per line for the location section:

location ~ /projects/so2006/(.*) { proxy_pass http://127.0.0.1:8001/$1; break; }  # Steppin' Out to Stop Domestic Violence
location ~ /projects/pw2007/(.*) { proxy_pass http://127.0.0.1:8002/$1; break; }  # Pendorwright 2007
location ~ /projects/kk2006/(.*) { proxy_pass http://127.0.0.1:8004/$1; break; }  # Karen Keiser 2006
location ~ /projects/sp2006/(.*) { proxy_pass http://127.0.0.1:8005/$1; break; }  # Sound Podcast Production & Consulting
location ~ /projects/ssb2007/(.*) { proxy_pass http://127.0.0.1:8006/$1; break; } # Shay Schual-Berke 2006
location / { proxy_pass http://127.0.0.1:81; } # Home

This makes writing a script to spew out all of my projects in a nice, clean table a trivial job of parsing the configuration.  As a benefit, it also analyzes the output of netstat and determines whether or not or or more servers Nginx is expecting to proxy might be down. Decorating the table with something from the Open Source Web Design project is left as an exercise.

#!/bin/env python
import re
import sys
from subprocess import Popen, PIPE

re_match = re.compile(r'^\s+location \~ (.*?)\s*\{.*http://([^\:]+)\:(\d+)/.*#([^#]+)$')
loc = open('/etc/nginx/nginx.conf')

ns_match = re.compile(r'^tcp\s+\d+\s+\d+\s+(0.0.0.0|127.0.0.1):(\d+)')
output = Popen(["netstat", "-an", "--protocol=inet"], stdout=PIPE).communicate()[0]
ports = []
for proc in output.split('\n'):
        g = ns_match.match(proc)
        if not g: continue
        ports.append(int(g.group(2)))

projects = []
for line in loc:
        g = re_match.match(line)
        if not g: continue
        (loc, site, port, proj) = g.group(1, 2, 3, 4)
        loc = loc.replace('(.*)', '')
        proj = proj.strip()
        projects.append((proj, loc, site, port, bool(int(port) in ports)))

print "Content-Type: text/html\r\n\r\n"
print "<html><body><table>"
for i in projects:
        print ('<tr><td><a href="%s">%s</a></td><td>'
               '<a href="http://%s:%s">Direct link</a></td><td>%s</td></tr>') % (
                i[1], i[0], i[2], i[3],
                (i[4] and '<span style="color: green">Up</span>'
                 or '<span style="color: red">Down</span>'))
print "</table></body></html>"
This entry was automatically cross-posted from Elf's technical journal, ElfSternberg.com
elfs: (Default)

FireWatir is my preferred testing suite for developing web applications.   Watir is a domain-language that describes things you do with a web browser: navigate to this page, find this input field, type in this text, find this button and click on it.  Combinded with a unit testing framework like Ruby’s Unit::Test, and you can write scripts that perform the basic functions of any application website and report back on what works and what fails.

The basis of Test-Driven Design is simple, but getting it right seems a little weird.  You really do have to write the tests first.   So I’m going to start with the two absolutely simplest scripts you’ve ever seen.  I haven’t even run the application builder script (rails <project> or django-admin.py startproject <project>) and already I know two things that it has to do.  But before that, let me show you the boilerplate I use for my test-driven work.  Below is a script that I put into a directory in my application project called setup.rb:

END {$ff.close if $ff} # close ff at completion of the tests

require 'rubygems'
require 'firewatir'
require 'test/unit'
require 'test/unit/ui/console/testrunner'
require 'net/http'
require 'json'
require 'optparse'

$options = {:address => 'http://localhost:8000/'}
OptionParser.new do |opts|
  opts.banner = "Usage: example.rb [options]"

  opts.on("-a", "--address", "Base address to test") do |v|
    $options[:address] = v
  end
end.parse!

This script is meant to be called by all testing scripts in the system.  It does two things: defines all of the libraries that the tests will need, defines a default address where we expect to find the development server, and accepts an alternative address as needed.

FireWatir invokes and drives Firefox.  The END clause at the top of this script banishes the window in which the tests were run when FireWatir has run all of its scripts.

Now, here’s the most basic test you’ll ever run.  I keep this in a file called, surprise, basic_test.rb.  This test has an important side-effect, which I’ll discuss in a moment.

require 'setup'
class TC_Basic < Test::Unit::TestCase
  include FireWatir

  def test_01_server_presence
    $ff = FireWatir::Firefox.start($options[:address])
    sleep 2
    # Do this rather than an assert; the tests cannot run if the server
    # is not present.
    if ($ff.title =~ /Page Load Error/) then
      puts "Could not contact server"
      exit
    end
  end

  def test_02_homepage_presence
    $ff.goto($options[:address])
    assert($ff.title =~ /SecretProject/)
  end
end

This file imports the setup script, which in turn does all of the argument parsing and setting up of libraries and stuff.  So that’s all taken care of.   It also defines a single test case, and two tests within that test case.  The first test is not really a test at all; it does the invocation of Firefox, sending it the address of the server.  If it cannot contact the server, it does not “fail” as a test should, instead it halts the tests right there.

The second test is much more fundamental, and is an accurate test.  It sets one variable (the home page URL), and asserts one true thing about the operation (the page’s title is set correctly)

One thing to note about these tests: I haven’t actually written the code these tests will test yet. That’s important.  My job, now, is to make these tests pass.  Once I’ve done that, I will go on to other things: logging in and registration, and then user interaction.

Each test will define one thing the user can actually do: list the user’s documents, pick a document, edit some detail of the document, save the document, delete the document, etc. etc.  That’s what TDD is for.  TDD is part of your documentation: it tells future testers what you expected your program to do well.

This entry was automatically cross-posted from Elf's technical journal, ElfSternberg.com
elfs: (Default)

I’m putting this here because I’m tired of losing it.  I hate having to look up every time the exact same recipe for “how do I create a new empty database for Django or Rails in MySQL?’  It’s in two different sections of the O’Reilly MySQL in a Nutshell book, and while I have both bookdarted and know where to find them, it’s nice to have them in just one place!  The  phrase ‘to X’ on line 4 is the database username, and the ‘identified by’ string there is your database password, in case you were wondering.

# mysql -u admin -p
password:
mysql> create database secretproject;
mysql> grant all on secretproject.* to secretproject identified by 'redacted';
mysql> flush privileges;
mysql> quit;
#
This entry was automatically cross-posted from Elf's technical journal, ElfSternberg.com

Profile

elfs: (Default)
Elf Sternberg

June 2025

S M T W T F S
1234567
891011121314
15161718192021
22232425262728
2930     

Most Popular Tags

Style Credit

Expand Cut Tags

No cut tags
Page generated Jun. 30th, 2025 05:01 am
Powered by Dreamwidth Studios