Published On: 2013-05-09 00:00:00
Go's standard lib comes with a number of useful tools. Each one of them written in an idiomatic fashion illustrating how to design good api's in Go. One of the most informative and useful are the io packages. both the "io" and "bufio" packages are examples of beautiful and idiomatic Go. I've recently decided to look at using the bufio.Scanner to build a tokenizer for a very simple lisp. The tokenizer's job is to emit a stream of tokens for each element of the lisp's syntax. The syntax is very basic with only the following tokens.
- Whitespace a series of newline, linefeed, tab or space characters
- StartExpr: a single '('
- EndExpr: a single ')'
- String: anything between double or single quotes with \ escaping the next character.
- Number: any series of numbers surrounded by whitespace
- Word: any series of characters surrounded by whitespace and starting with a-z or A-Z
The tokenizer we are going to build should emit a stream of these tokens for us while also keeping track of where in the file these tokens occur. [Go's bufio package](http://golang.org/pkg/bufio) has a useful general purpose Scanner type that splits an io.Reader into a series of byte slices for you. Our first task is to figure out how to use the Scanner to split up any reader into a stream of slices representing each of these tokens. The Scanner type has 5 methods. We'll be focusing on just 4 of them.
- The Scan method scans the stream looking for the next byte slice to emit. It returns false if the Scan hit an error or EOF.
- The Bytes method returns the current byte slice after Scan has run.
- The Err method returns the last non io.EOF error encountered by Scan.
- The Split method sets the SplitFunc to use for the next Scan.
Together these give us all the tools necessary to tokenize any io.Reader. The first step is to create a Scanner to use.
package main
import (
"strings"
"bufio"
"fmt"
)
func main() {
s := bufio.NewScanner(strings.NewReader("(foo 1 'bar')\n(baz 2 'quux')"))
for s.Scan() {
tok := s.Bytes()
fmt.Println("Found token ", tok)
}
}
You can play with this code here http://play.golang.org/p/fhOV_4fVOI. Out of the box the scanner splits the io.Reader into lines which is obviously not what we want. In order to let us specify our own rules for splitting bufio.Scanner uses composition. We can pass in our splitting logic in the form of a SplitFunc. A SplitFunc takes a candidate slice of data for splitting and returns how far to advance the position in the reader stream, the byte slice to return for this split, and an error if any is needed. Below you'll find the basic skeleton for our splitting logic.
package main
import (
"strings"
"bufio"
"fmt"
)
func consumeWord(data []byte) (int, []byte, error) {
// TODO
return 0, nil, nil
}
func consumeWhitespace(data []byte) (int, []byte, error) {
// TODO
return 0, nil, nil
}
func consumeNum(data []byte) (int, []byte, error) {
// TODO
return 0, nil, nil
}
func consumeString(data []byte) (int, []byte, error) {
// TODO
return 0, nil, nil
}
func main() {
s := bufio.NewScanner(strings.NewReader("(foo 1 'bar')\n(baz 2 'quux')"))
split := func(data []byte, atEOF bool) (advance int, token []byte, err error) {
// Because our grammar is simple we can switch off the first
// character in the reader.
switch data[0] {
case '(', ')':
advance, token, err = 1, data[:1], nil
case '"', '\'':
advance, token, err = consumeString(data)
case '0', '1', '2', '3', '4', '5', '6', '7', '8', '9':
advance, token, err = consumeNum(data)
case ' ', '\n', '\r', '\t':
advance, token, err = consumeWhitespace(data)
default:
advance, token, err = consumeWord(data)
}
return
}
s.Split(split)
for s.Scan() {
tok := s.Bytes()
fmt.Println("Found token ", tok)
}
}
The split func inspects the start of our candidate slice and determines how it should handle it. The parens case is easy so we implented it first by telling the Scanner to advance by one byte and emit a byte slice of size one containing the paren and nil for the error. We've defined names for consume functions for each of our other tokens but left them unimplemented so far. We'll start with consumeWord to give an example of how they should work.
func consumeWord(data []byte) (int, []byte, error) {
var accum []byte
for i, b := range data {
if b == ' ' || b == '\n' || b == '\t' || b == '\r' {
return i, accum, nil
} else {
accum = append(accum, b)
}
}
return 0, nil, nil
}
Tokenizing a Word token is easy. A Word starts with any character that isn't a quote, paren or Number and they continue until you hit whitespace. Our switch statement handles the quote, paren, and number cases specially and anything left over is a Word. Then we call consumeWord with the candidate slice. consumeWords job is to scan the candidate for whitespace accumulating as we go. We could if we want bypass the accumulator but this was easier to understand for now. If no whitespace is seen then we don't have a full word yet so return 0, nil, nil. If we do encounter any whitespace then return the amount to advance, not including the whitespace, as well the word we've accumulated and nil. The scanner will keep trying our split function on increasing slice sizes until it either gets an error, non-zero advance or hits the end of the stream. We code similar consume functions for the Whitespace, String, and Number tokens.
func consumeWhitespace(data []byte) (int, []byte, error) {
var accum []byte
for i, b := range data {
if b == ' ' || b == '\n' || b == '\t' || b == '\r' {
accum = append(accum, b)
} else {
return i, accum, nil
}
}
return 0, nil, nil
}
func consumeNum(data []byte) (int, []byte, error) {
var accum []byte
for i, b := range data {
if '0' <= b && b <= '9' {
accum = append(accum, b)
} else {
return i, accum, nil
}
}
return len(accum), accum, nil
}
func consumeString(data []byte) (int, []byte, error) {
delim := data[0]
skip := false
accum := []byte{data[0]}
for i, b := range data[1:] {
if b == delim && !skip {
return i + 2, accum, nil
}
skip = false
if b == '\\' {
skip = true
continue
}
accum = append(accum, b)
}
return 0, nil, nil
}
If you play with this code here http://play.golang.org/p/dksq4YjJtb. then you'll see it emit one of each of our tokens.
We still aren't done though. This Scanner while useful doesn't tell us our Token Locations. We need it to tell us the Line and column that each token starts on. To do this we'll use composition again. Only this time we'll compose two structs using Go's embedded fields.
type lineTrackingReader struct {
// Embedded Scanner so our LineTrackingReader can be used just like
// a Scanner.
*bufio.Scanner
lastL, lastCol int // Position Tracking fields.
l, col int
}
func newTrackingReader(r io.Reader) *lineTrackingReader {
s := bufio.NewScanner(r)
rdr := &lineTrackingReader{
Scanner: s,
}
split := func(data []byte, atEOF bool) (advance int, token []byte, err error) {
if rdr.l == 0 { // Initiliaze our position tracking fields using a split like a closure.
rdr.l = 1
rdr.col = 1
rdr.lastL = 1
rdr.lastCol = 1
}
switch data[0] {
case '(', ')':
advance, token, err = 1, data[:1], nil
case '"', '\'': // TODO(jwall): Rune data?
advance, token, err = consumeString(data)
case '0', '1', '2', '3', '4', '5', '6', '7', '8', '9':
advance, token, err = consumeNum(data)
case ' ', '\n', '\r', '\t':
advance, token, err = consumeWhitespace(data)
default:
advance, token, err = consumeWord(data)
}
// If we are advancing then we should see if there are any line
// endings here
if advance > 0 {
rdr.lastCol = rdr.col
rdr.lastL = rdr.l
for _, b := range data[:advance] {
if b == '\n' || atEOF { // Treat eof as a newline
rdr.l++
rdr.col = 1
}
rdr.col++
}
}
return
}
s.Split(split)
return rdr
}
func main() {
s := newTrackingReader(strings.NewReader("(foo 1 'bar')\n(baz 2 'quux')"))
for s.Scan() {
tok := s.Bytes()
fmt.Printf(
"Found token %q at line #%d col #%d\n",
string(tok), s.Line(), s.Column)
}
}
func (l *lineTrackingReader) Line() int {
return l.lastL
}
func (l *lineTrackingReader) Column() int {
return l.lastCol
}
You can play with the finished code here http://play.golang.org/p/_omf7AGefg.
Tags:
Published On: 2013-05-09 00:00:00
Go's standard lib comes with a number of useful tools. Each one of them written in an idiomatic fashion illustrating how to design good api's in Go. One of the most informative and useful are the io packages. both the "io" and "bufio" packages are examples of beautiful and idiomatic Go. I've recently decided to look at using the bufio.Scanner to build a tokenizer for a very simple lisp. The tokenizer's job is to emit a stream of tokens for each element of the lisp's syntax. The syntax is very basic with only the following tokens.
- Whitespace a series of newline, linefeed, tab or space characters
- StartExpr: a single '('
- EndExpr: a single ')'
- String: anything between double or single quotes with \ escaping the next character.
- Number: any series of numbers surrounded by whitespace
- Word: any series of characters surrounded by whitespace and starting with a-z or A-Z
The tokenizer we are going to build should emit a stream of these tokens for us while also keeping track of where in the file these tokens occur. [Go's bufio package](http://golang.org/pkg/bufio) has a useful general purpose Scanner type that splits an io.Reader into a series of byte slices for you. Our first task is to figure out how to use the Scanner to split up any reader into a stream of slices representing each of these tokens. The Scanner type has 5 methods. We'll be focusing on just 4 of them.
- The Scan method scans the stream looking for the next byte slice to emit. It returns false if the Scan hit an error or EOF.
- The Bytes method returns the current byte slice after Scan has run.
- The Err method returns the last non io.EOF error encountered by Scan.
- The Split method sets the SplitFunc to use for the next Scan.
Together these give us all the tools necessary to tokenize any io.Reader. The first step is to create a Scanner to use.
package main
import (
"strings"
"bufio"
"fmt"
)
func main() {
s := bufio.NewScanner(strings.NewReader("(foo 1 'bar')\n(baz 2 'quux')"))
for s.Scan() {
tok := s.Bytes()
fmt.Println("Found token ", tok)
}
}
You can play with this code here http://play.golang.org/p/fhOV_4fVOI. Out of the box the scanner splits the io.Reader into lines which is obviously not what we want. In order to let us specify our own rules for splitting bufio.Scanner uses composition. We can pass in our splitting logic in the form of a SplitFunc. A SplitFunc takes a candidate slice of data for splitting and returns how far to advance the position in the reader stream, the byte slice to return for this split, and an error if any is needed. Below you'll find the basic skeleton for our splitting logic.
package main
import (
"strings"
"bufio"
"fmt"
)
func consumeWord(data []byte) (int, []byte, error) {
// TODO
return 0, nil, nil
}
func consumeWhitespace(data []byte) (int, []byte, error) {
// TODO
return 0, nil, nil
}
func consumeNum(data []byte) (int, []byte, error) {
// TODO
return 0, nil, nil
}
func consumeString(data []byte) (int, []byte, error) {
// TODO
return 0, nil, nil
}
func main() {
s := bufio.NewScanner(strings.NewReader("(foo 1 'bar')\n(baz 2 'quux')"))
split := func(data []byte, atEOF bool) (advance int, token []byte, err error) {
// Because our grammar is simple we can switch off the first
// character in the reader.
switch data[0] {
case '(', ')':
advance, token, err = 1, data[:1], nil
case '"', '\'':
advance, token, err = consumeString(data)
case '0', '1', '2', '3', '4', '5', '6', '7', '8', '9':
advance, token, err = consumeNum(data)
case ' ', '\n', '\r', '\t':
advance, token, err = consumeWhitespace(data)
default:
advance, token, err = consumeWord(data)
}
return
}
s.Split(split)
for s.Scan() {
tok := s.Bytes()
fmt.Println("Found token ", tok)
}
}
The split func inspects the start of our candidate slice and determines how it should handle it. The parens case is easy so we implented it first by telling the Scanner to advance by one byte and emit a byte slice of size one containing the paren and nil for the error. We've defined names for consume functions for each of our other tokens but left them unimplemented so far. We'll start with consumeWord to give an example of how they should work.
func consumeWord(data []byte) (int, []byte, error) {
var accum []byte
for i, b := range data {
if b == ' ' || b == '\n' || b == '\t' || b == '\r' {
return i, accum, nil
} else {
accum = append(accum, b)
}
}
return 0, nil, nil
}
Tokenizing a Word token is easy. A Word starts with any character that isn't a quote, paren or Number and they continue until you hit whitespace. Our switch statement handles the quote, paren, and number cases specially and anything left over is a Word. Then we call consumeWord with the candidate slice. consumeWords job is to scan the candidate for whitespace accumulating as we go. We could if we want bypass the accumulator but this was easier to understand for now. If no whitespace is seen then we don't have a full word yet so return 0, nil, nil. If we do encounter any whitespace then return the amount to advance, not including the whitespace, as well the word we've accumulated and nil. The scanner will keep trying our split function on increasing slice sizes until it either gets an error, non-zero advance or hits the end of the stream. We code similar consume functions for the Whitespace, String, and Number tokens.
func consumeWhitespace(data []byte) (int, []byte, error) {
var accum []byte
for i, b := range data {
if b == ' ' || b == '\n' || b == '\t' || b == '\r' {
accum = append(accum, b)
} else {
return i, accum, nil
}
}
return 0, nil, nil
}
func consumeNum(data []byte) (int, []byte, error) {
var accum []byte
for i, b := range data {
if '0' <= b && b <= '9' {
accum = append(accum, b)
} else {
return i, accum, nil
}
}
return len(accum), accum, nil
}
func consumeString(data []byte) (int, []byte, error) {
delim := data[0]
skip := false
accum := []byte{data[0]}
for i, b := range data[1:] {
accum = append(accum, b)
if b == delim && !skip {
return i + 2, accum, nil
}
skip = !skip && b == '\\'
}
return 0, nil, nil
}
If you play with this code here http://play.golang.org/p/dksq4YjJtb. then you'll see it emit one of each of our tokens.
We still aren't done though. This Scanner while useful doesn't tell us our Token Locations. We need it to tell us the Line and column that each token starts on. To do this we'll use composition again. Only this time we'll compose two structs using Go's embedded fields.
type lineTrackingReader struct {
// Embedded Scanner so our LineTrackingReader can be used just like
// a Scanner.
*bufio.Scanner
lastL, lastCol int // Position Tracking fields.
l, col int
}
func newTrackingReader(r io.Reader) *lineTrackingReader {
s := bufio.NewScanner(r)
rdr := &lineTrackingReader{
Scanner: s,
}
split := func(data []byte, atEOF bool) (advance int, token []byte, err error) {
if rdr.l == 0 { // Initiliaze our position tracking fields using a split like a closure.
rdr.l = 1
rdr.col = 1
rdr.lastL = 1
rdr.lastCol = 1
}
switch data[0] {
case '(', ')':
advance, token, err = 1, data[:1], nil
case '"', '\'': // TODO(jwall): Rune data?
advance, token, err = consumeString(data)
case '0', '1', '2', '3', '4', '5', '6', '7', '8', '9':
advance, token, err = consumeNum(data)
case ' ', '\n', '\r', '\t':
advance, token, err = consumeWhitespace(data)
default:
advance, token, err = consumeWord(data)
}
// If we are advancing then we should see if there are any line
// endings here
if advance > 0 {
rdr.lastCol = rdr.col
rdr.lastL = rdr.l
for _, b := range data[:advance] {
if b == '\n' || atEOF { // Treat eof as a newline
rdr.l++
rdr.col = 1
}
rdr.col++
}
}
return
}
s.Split(split)
return rdr
}
func main() {
s := newTrackingReader(strings.NewReader("(foo 1 'bar')\n(baz 2 'quux')"))
for s.Scan() {
tok := s.Bytes()
fmt.Printf(
"Found token %q at line #%d col #%d\n",
string(tok), s.Line(), s.Column)
}
}
func (l *lineTrackingReader) Line() int {
return l.lastL
}
func (l *lineTrackingReader) Column() int {
return l.lastCol
}
You can play with the finished code here http://play.golang.org/p/_omf7AGefg.
Tags:
Published On: 2013-02-22 12:14:00
A number of folks have requested I write up the story of how I went from homeless with kids and a wife to a career as a software developer. The story could start from any number of places but I'll start mine as a young teenager. Our family didn't have a lot of money. I wasn't really aware of it since my parents did a good job of ensuring we always had food and a place to live. One year though they took the tax return and used it to purchase a computer. That computer opened up a whole world to me. It came with a copy of gw-basic and some games written in basic that I could play. Not only could I play them but I could see the source code when I started them up. My first ventures into programming were modifying those first games. Mostly so I could cheat. This is s a perfectly hacker like mentality. The challenge of cheating was more interesting than playing the actual game.
Before long though, I started to wonder if I could do more. I also loved science fiction at the time and I had read a book, whose title I no longer remember, that featured Conways Game of Life as a part of the plot. The rules, of which there were only 4, seemed very well suited to a program so my first truly creative endeavor with basic was programming a version of the Game of Life. It would be many years later before I discovered on the internet how popular this game was with geeky computer people like me.
Fast forward a few years and I'm on my way to college. I wasn't as you might expect going for a computer science degree though. I wanted to be a virus hunter for the CDC. I loved biology and viruses were just really cool. I got a 95% paid scholarship to Missouri Baptist College in St. Louis and away I go. While I'm there I meet a wonderful girl, the love of my life. We end up getting married and I take a fulltime job at the college which had two benefits that were really appealing. It was a full ride tuition to the school. It was fulltime income to support my new family. Things seemed like they were progressing just they way I wanted.
What I didn't know was how hard it is to attend school, work full-time, and keep a marriage going. My progression in school slowed and to top it off I began to get the feeling that working in a biology lab somewhere probably wasn't going to be what I really wanted to do with my life. Cue life crisis. Sudenly I didn't really know what I should do next. I was coasting. The whole time my hobby with computers continued to hang in the background. Eventually the college ends up laying me off. I lose the full ride, I no longer have the scholarships since I had to give those up when I took the full ride. I have a family with 4 kids and no job. It all starts crashing down. What am I supposed to do now?
Of course when you don't have a job it's hard to make the rent, buy food, and otherwise make ends meet. I go through a succession of different jobs never really holding on to any of them until finally it all culminates with my family being homeless. My wife is sticking by me this whole time never once questioning her choice to marry me. Something that to this day still amazes me. We end up having to live with family and I'm working at labor ready to make ends meet. And the whole while my hobby is still hanging in the background. There were days when I thought I was going to have to sell my computer to get food. My wife always insisted I keep it though. I think she knew this was going to be the key to getting out of the whole we were in someday.
So there I am I have a computer, an internet connection. And pretty much nothing else. What's a Guy supposed to do when he's not job searching? Of Course! Hang out on the internet. Two very influential Open Source projects became my life at this point. Blender and Planeshift. I hung out in their irc chatrooms and message boards I downloaded the code and I started hacking in earnest. I learned C and C++ on those projects and more importantly I interacted with people who not only knew how to code but did it professionally and managed a large project. Keep in mind this is still all just my hobby.
Eventually I get a full time job working for a company managing networks in school districts in Southern Illinois and Missouri. We start to pull out of the hole we were in financially and even purchase a house. A dirt cheap house that needed a lot of work but things were looking up. Then disaster strikes again. My boss at the company had never quite figured out what to do with me. He knew I had some experience as a programmer in my hobby and I knew my way around a network. He couldn't quite decide if I was going to be a part of the new programming division or just another network admin. Eventually that indecision led to my being let go. Which could have been a problem except that I had made some contacts with people through the job and was able to immediately get work as a contractor for a local company doing web development in perl.
I start updating my blog with what I learn while working and even publish a CPAN module or two. I'm pretty satisified with where I landed and figure this is how my life will be for the forseeable future. I'm not looking for a job but my blog and personal network in open source have started to get me a little attention. Not much but enough that one day a recruiter from Chicago cold calls me. I've gotten used to being a small time operator and getting refused at all my attempts to get a job in the industry so I'm assuming of course that this is a fluke and I'll never get the job. I do a phone interview, then another phone interview, then they call me in for a face to face. I start to believe that maybe I have a chance here. After the face to face I wait and they call me back saying I've got the job. Not only that but I discover the company is getting bought by Google pending approval from various governments. Long story short I get hired, the company purchase goes through, I survive the transition to Google and I find myself working at one of the top companies in my new career.
So why is this story interesting? I think it highlights a few things that are still true about the Software Development industry today.
- The most important contributor to your success is your desire to learn and your persistence.
- Programming has more free and self driven avenues for learning than any other subject matter. And the resources have only gotten better since I started.
- OpenSource is a merit based approach to gaining credentials and learning in this field.
So, if your passion is Software Development, there is literally nothing standing in your way except you.
One Last thing
Throughout all of the above events I had family and friends praying for me. I'm a devout christrian and I strongly believe I'm at Google now because God wants me there. Their support was a large part of my success as well.
Tags:
Published On: 2013-02-13 22:24:00
Hello there,
I haven't updated this blog in over two years. That's a long time for something on the internet. I still get traffic for various articles though so I figured I should probably get back into the swing of things.
I've been working at Google as some of you may already know. I also still do a lot of hacking in my spare time. Lately most of it has been in Go. But I do the occasional thing in Javascript, Erlang, and Haskell as well.
So lets take stock shall we?
- Latest Projects:
- Revamped this blog's html and css. I do hope the colors and typography are better. I'll be continuing to tweak it over the next few weeks.
- Teaching some classes in Downtown chicago for friends.
I hope to write some articles about Go soon as well. The first will probably be about go-html-transform.
I've also been heavily active in church. We attend FBC Lansing now. It's a healthy church and I now serve on the Deacon board as well as teaching Sunday School and Youth Bible study.
Tags:
Published On: 2009-08-29 00:16:37
Design/Coding rules
Data and Operations should be seperate
Workflows are awesome
Work from the outside in
- Then from the Inside out
TDD TDD TDD
Keep the IO partitioned and at the edges
API's need to be *tight
- More Permissive == more Headaches
Fuzzy states are bad
Every if has an else
Boilerplate is bad (boring to review hard to get right)
Tags:
Published On: 2009-06-27 13:13:20
I just recently upgraded my home desktop. It was nearing 10 years in age and desperately whispering to me in my sleep that it wasn't going to last much longer. The new machine is a Quad core AMD phenom with A Gigabyte board and 8 gig of ram. This of course necessitated a new OS to go along with the shiny new hardware, so along comes Ubuntu Jaunty with its 64-bit joy and the shiny new KDE4 desktop. I had previously switched from kde to gnome despite being a kde fan for years because gnome had just begun to feel more cohesive. I still disliked the lack of configurability as compared to gnome but overall gnome felt better. But with the new kde4 I've come back. Plasma and the kde desktop have really upped the game. The whole experience has vastly improved and kde no longer feels like it's lagging behind but has leaped ahead of the competition. If you haven't checked it out yet you should give it a try.
Tags:
Published On: 2009-06-16 23:33:58
In my
last post I walked you through creating a basic nitrogen element. In this one I'll be covering some of the more advanced topics in nitrogen elements.
- Event handlers and delegation
- scripts and dynamic javascript postbacks
Nitrogen Event handlers
Nitrogen event handlers get called for any nitrogen event. A nitrogen event is specified by assigning #event to an actions attribute of a nitrogen element. The event handler in the pages module will get called with the postback of the event. Postbacks are an attribute of the event record and define the event that was fired. To handle the event you create an event function in the target module that matches your postback. For Example:
% given this event
#event{ type=click, postback={click, Id} }
% this event function would handle it
event({click, ClickedId}) ->
io:format("I [~p] was clicked", [ClickedId]) .
Erlangs pattern matching makes it especially well suited for this kind of event based programming. The one annoying limitation of this event though is that each page has to handle it individually. You could of course create a dispatching module that handled the event for you but why when nitrogen already did it for you. You can delegate an event to a specific module by setting the delegate attribute to the atom identifying that module.
% delgated event
#event{ type=click, postback={click, Id}, delegate=my_module }
You can delgate to any module you want. I use the general rule of thumb that if the event affects other elements on the page then the page module should probably handle it. If, however, the event doesn't affect other elements on the page then the element's module can handle it.
Scripts and Dynamic Postback
Now lets get make it a little more interesting. Imagine a scenario where we want to interact with some javascript on a page and dynamically generate data to send back to nitrogen. As an example lets create a silly element that grabs the mouse coordinates of a click on the element and sends that back to nitrogen. A first attempt might look something like so:
-record(silly, {?ELEMENT_BASE(element_silly)}).
And the module is likewise simple:
-module(element_silly).
-compile(export_all).
-include("elements.hrl").
-include_lib("nitrogen/include/wf.inc").
render(ControlId, R) ->
Id = wf:temp_id(),
%% wait!! where do we get the loc from?!
ClickEvent = #event{type=click, postback={click, Loc}}
Panel = #panel{id=Id, style="width:'100px' height='100px'",
actions=ClickEvent}, element_panel:render(Panel).
event({click, Loc}) ->
wf:update(body, wf:f("you clicked at point: ~s", Loc)).
Well of course you spot the problem here. Since the click happens client side we don't know what to put in the Loc variable for the postback. A typical postback won't work because the data will be generated in the client and not the Nitrogen server. So how could we get the value of the coordinates sent back? The javascript to grab the coordinates with jquery looks like this:
var coord = obj('me').pageX + obj('me').pageY;
To plug that in to the click event is pretty easy since action fields in an event can hold other events or javascript or a list combining both:
Script = "var coord = obj('me').pageX + obj('me').pageY;",
ClickEvent = #event{type=click, postback={click, Loc}, actions=Script}
Now we've managed to capture the coordinates of the mouse click, but we still haven't sent it back to the server. This javascript needs a little help. What we need is a drop box. Lets enhance our element with a few helpers:
-module(element_silly).
-compile(export_all).
-include("elements.hrl").
-include_lib("nitrogen/include/wf.inc").
render(ControlId, R) ->
Id = wf:temp_id(),
DropBoxId = wf:temp_id(),
MsgId = wf:temp_id(),
Script = wf:f("var coord = obj('me').pageX + obj('me').pageY; $('~s').value = coord;",
[DropBoxId]),
ClickEvent = #event{type=click, postback={click, Id, MsgId},
actions=Script},
Panel = #panel{id=Id, style="width:'100px'; height='100px'",
actions=ClickEvent, body=[#hidden{id=DropBoxId},
#panel{id=MsgId}]},
element_panel:render(Panel).
event({click, Id, Msg}) ->
Loc = hd(wf:q(Id)),
wf:update(Msg, wf:f("you clicked at point: ~s", Loc)).
Ahhh there we go. Now our element when clicked will:
- use javascript to grab the coordinates of the mouse click
- use javascript to store those coordinates in the hidden element
- use a postback to send the click event back to a nitrogen event handler with the id of the hidden element where it stored the coordinates.
We have managed to grab dynamically generated data from the client side and drop it somehwere that nitrogen can retrieve it. In the process we have used an event handler, custom javascript, and dynamic javascript postbacks.
Edit: Corrected typo - June 16, 2009 at 11:40 pm
Tags:
Published On: 2009-05-22 19:05:57
Nitrogen is a web framework written in erlang for Fast AJAX Web applications. You can get
Nitrogen on github Nitrogen comes with a set of useful controls, or elements in nitrogen parlance, but if you are going to do anything more fancy than a basic hello world you probably want to create some custom controls. This tutorial will walk you through the ins and outs of writing a custom element for Nitrogen. We will be creating a simple notification element similar to one I use in the Iterate! project. It will need to be able to:
- show a message
- have a way to dismiss it
- and optionally expire and disappear after a configurable period of time
Every Nitrogen element has two main pieces: the Record and the Module. I'll go through each in order and walk you through creating our notification element.
The Record
The record defines all the state required to create a nitrogen element. Every record needs a certain base set of fields. These fields can be added to your record with the ?ELEMENT_BASE macro. The macro is available in the nitrogen include file wf.inc. That include file also gives you access to all the included nitrogen element records. Below you can see the record definition for our notify element. Since it is very simple in it's design it only needs the base elements and two additional fields. expire to handle our optional expiration time and default to false to indicate no expiration. msg to hold the contents of our notification.
%Put this line in an include file for your elements
-record(notify, {?ELEMENT_BASE(element_notify), expire=false, msg}).
% put these at the top of your elements module
-include_lib("nitrogen/include/wf.inc").
% the above mentioned include file you may call it whatever you want
-include("elements.inc").
The ELEMENT_BASE macro gives your element several fields and identifies for the element which module handles the rendering of your nitrogen element. You can specify any module you want but the convention is to name the module with element_element_name. The fields provided are: id, class, style, actions, and show_if. You can use them as you wish when it comes time to render your element. Which brings us to the module.
The Module
Of the two pieces of a nitrogen element the module does the manual labor. It renders and in some cases defines the handlers for events fired by the element. The module must export a render/2 function. This function will be called whenever nitrogen needs to render a particular instance of your element. It's two arguments are: The ControlId, and the Record defining this element instance. Of these the ControlID is probably the least understood. It is passed into your render method by nitrogen and is the assigned HTML Id for your particular element. This is important to understand because, when you call the next render method in your elements tree, you will have to pass an ID on. The rule of thumb I use is that if you want to use a different Id for your toplevel element then you can ignore the ControlId. Otherwise you should use it as the id for your toplevel element in the control. So your element's module should start out with something like this:
-module(element_notify).
-compile(export_all).
-include_lib("nitrogen/include/wf.inc").
-include("elements.hrl").
% give us a way to inspect the fields of this elements record
% useful in the shell where record_info isn't available
reflect() -> record_info(fields, notify).
% Render the custom element
render(ControlId, R) ->
% get a temp id for our notify element instance
Id = ControlId,
% Our toplevel of the element will be a panel (div)
Panel = #panel{id=Id},
% the element_panel module is used to render the panel element
element_panel:render(Id, Panel),
% Or use the alternative method:
Module = Panel#panel.module,
Module:render(Id, Panel).
Notice that the records module attribute tells us what module we should call to render the element in the alternative method. In our case we will just hardcode the module since it's known to us. So now we have a basic element that renders a div with a temp id to our page. That's not terribly useful though. We actually need this element to render our msg, and with some events attached. Lets add the code to add our message to the panels contents.
Panel = #panel{id=Id, body=R#notify.msg},
element_panel:render(ControlId, Panel)
Now whatever is in the msg attribute of our notify record will be in the body of the panel when it gets rendered. All we need is a way to dismiss it. A link should do the trick. But now we have a slight problem. In order to add our dismiss link we need to add it to the body of the Panel. but the msg is already occupying that space. We could use a list and prepend the link to the end of the list for the body but that doesn't really give us a lot of control over styling the element. what we really need is for the msg to be in an inner panel and the outer panel will hold any controls the element needs.
Link = #link{text="dismiss"},
InnerPanel = #panel{body=R#notify.msg},
Panel = #panel{id=Id, body=[InnerPanel,Link]},
element_panel:render(ControlId, Panel)
Our link doesn't actually dismiss the notification yet though. To add that we need to add a click event to the link. Nitrogen has a large set of events and effects available. You can find them . We will be using the click event and the hide effect.
Event = #event{type=click,
actions=#hide{effect=blind, target=Id}},
Link = #link{text="dismiss", actions=Event},
Now our module should look something like this:
-module(element_notify).
-compile(export_all).
-include_lib("nitrogen/include/wf.inc").
-include("elements.hrl").
% give us a way to inspect the fields of this elements record
% useful in the shell where record_info isn't available
reflect() -> record_info(fields, notify).
% Render the custom element
render(ControlId, R) ->
% get a temp id for our notify element instance
Id = ControlId,
% Our toplevel of the element will be a panel (div)
Event = #event{type=click, actions=#hide{effect=blind, target=Id}},
Link = #link{text="dismiss", actions=Event},
InnerPanel = #panel{body=R#notify.msg},
Panel = #panel{id=Id, body=[InnerPanel,Link]},
% the element_panel module is used to render the panel element
element_panel:render(Id, Panel).
This is a fully functional nitrogen element. But it's missing a crucial feature to really shine. Our third feature for this element was an optional expiration for the notification. Right now you have to click dismiss to get rid of the element on the page. But sometimes we might want the element to go away after a predetermined time. This is what our expire record field is meant to determine for us. There are three possible cases for this field.
- set to false (the default)
- set to some integer (the number of seconds after which we want to go away)
- set to anything else (the error condition)
This is the kind of thing erlang's case statement was made for:
case R#notify.expire of
false ->
undefined;
N when is_integer(N) ->
% we expire in this many seconds
wf:wire(Id, #event{type='timer', delay=N, actions=#hide{effect=blind, target=Id}});
_ ->
% log error and don't expire
undefined
end
Notice the wf:wire statement. wf:wire is an alternate way to add events to a nitrogen element. Just specify the id and then the event record/javascript string you want to use. I've noticed that for events of type timer wf:wire works better than assigning them to the actions field of the event record. No idea why because I have not looked into it real closely yet. Now our module looks like this: -module(element_notify).
-compile(export_all).
-include_lib("nitrogen/include/wf.inc").
-include("elements.hrl").
% give us a way to inspect the fields of this elements record
% useful in the shell where record_info isn't available
reflect() ->record_info(fields, notify).
% Render the custom element
render(_, R) ->
% get a temp id for our notify element instance
Id = ControlId,
% Our toplevel of the element will be a panel (div)
case R#notify.expire of
false ->
undefined;
N when is_integer(N) ->
% we expire in this many seconds
wf:wire(Id, #event{type='timer', delay=N, actions=#hide{effect=blind, target=Id}});
_ ->
% log error and don't expire
undefined
end,
Event = #event{type=click, actions=#hide{effect=blind, target=Id}},
Link = #link{text="dismiss", actions=Event},
InnerPanel = #panel{body=R#notify.msg},
Panel = #panel{id=Id, body=[InnerPanel,Link]},
% the element_panel module is used to render the panel element
element_panel:render(ControlId, Panel).
We have now fulfilled all of our criteria for the element. It shows a message of our choosing. It can be dismissed with a click. And it has an optional expiration. One last thing to really polish it off though would to allow styling through the use of css classes. The ELEMENT_BASE macro we used in our record definition gives our element a class field. We can use that to set our Panel's class, allowing any user of the element to set the class as they wish like so:
Panel = #panel{id=Id, class=["notify ", R#notify.class],
body=[InnerPanel,Link]},
This gives us the final module for our custom element:
-module(element_notify).
-compile(export_all).
-include_lib("nitrogen/include/wf.inc").
-include("elements.hrl").
% give us a way to inspect the fields of this elements record
% useful in the shell where record_info isn't available
reflect() -> record_info(fields, notify).
% Render the custom element
render(_, R) ->
% get a temp id for our notify element instance
Id = ControlId,
% Our toplevel of the element will be a panel (div)
case R#notify.expire of
false ->
undefined;
N when is_integer(N) ->
% we expire in this many seconds
wf:wire(Id, #event{type='timer', delay=N, actions=#hide{effect=blind, target=Id}});
_ ->
% log error and don't expire
undefined
end,
Event = #event{type=click, actions=#hide{effect=blind, target=Id}},
Link = #link{text="dismiss", actions=Event},
InnerPanel = #panel{body=R#notify.msg},
Panel = #panel{id=Id, class=["notify ", R#notify.class],
body=[InnerPanel,Link]},
% the element_panel module is used to render the panel element
element_panel:render(ControlId, Panel).
I will cover delegated events and more advanced topics in a later tutorial.
Tags:
Published On: 2009-05-01 01:17:21
I have been doing some work on timeseries data in erlang for iterate graphs and
found the calendar module in stdlib to be lacking in some useful features.
So being the helpful hacker I am I created a utility module to wrap calendar
and be a little more userfriendly. May I present date_util.erl:
Tags:
Published On: 2009-04-26 20:52:39
I've been thinking lately about the differences between
OO and
FP. FP languages don't really support the OO model because of a difference in design. Some try but the "true" functional languages make no such effort and for good reason. One of the primary principles of OO programming is the black box. An object is meant to represent some entity in the system. All the state and valid actions for that entity are encapsulated in the Object and the only visibility you have into them is the interface the object provides to the outside world. Programs tend to be defined as a set of interactions between objects. FP takes a different approach. In FP data is not modifiable only transformable by functions. In FP the functions expect certain input and give back certain output for that input. They certainly don't maintain any state on their own. You can't have an object if you can't have modifiable internal state. In OO you are encouraged to group together state and activity on that state. In OO you don't think so much about state as you do about entities. Person, Car, ATM machine. All of these are commonly referenced objects in an OO tutorial or textbook. Consumers of your object are encouraged not to think about the state of a person or a car or an ATM machine. Instead they think in terms of allowable interactions with that object: Talk, Drive, Withdraw Money. Within OO code there are a huge number of possible orders of various interactions possible. So large they are just about impossible to completely test or even visualize. For example say I'm programming a person object. I've given the person object a handshake method. However, depending on the internal state of the person object that method may or may not work the same way or be supposed to be called. Maybe the person object is in a bad mood, in which case the handshake method is non-functional. How do we handle that? Hrmm, so first before calling handshake on the person object we should call the offer hand method. No wait, before we call the offer hand method, perhaps we should look at their face first to gauge their mood. This could get complicated pretty quickly. Furthermore there is nothing in the OO language that allows us to specify that the offer hand method should happen first. We obviously need to know quite a bit about the state of that person object in order to properly interact with it. Contrast that with an FP approach. This gets a little bit simpler for some people to visualize suddenly. Rather than an object, they see state and a set of possible transformations of that state. the state may be something like this. person1 mood, person2 mood. and there is an interact function. If the person1 and person2 mood is good then the interact function offers hand for handshake and the two shake hands then interact returns the new person1 mood and person2 mood. Or if the mood of one of them is bad then do some other action then return new person1 mood and person 2 mood. By placing the emphasis on the state involved the coder has reduced the problem to a set of possible transformations based on that state. In OO, an object's methods define the interface. but passing the same input to an object will not always result in the same output. Most of the time methods modify internal state and the return depends on the internal state. At any time in your code the internal state of an object must be treated as an unknown. This means the return of your objects method must also be treated as an unknown. Every object has to treat every other object like it has some sort of mental disease and could have a psychotic break any moment. It could do just about anything and if you didn't define the proper response to what they do then who knows what might happen. In FP you are encouraged to think in terms of state transformations. You are forced to consider what the state you are dealing with is and then transform that state appropriately. Since the state is of primary importance after a while FP feels more natural than OO to certain mindsets. I'm discovering that I rather like the FP mindset and miss OO less and less these days.
Glossary:
- FP
- Functional Programming
- OO
- Object Oriented
Tags:
Published On: 2009-04-16 19:41:41
I had an epiphany or sorts the other day. While working on iterate I realized that I was going to need to upgrade the schema of my mnesia tables. Schema changes on databases often mean bringing down the application. At least they usually have in my experience anyway. I hate to upgrade databases. That is I did until I met erlang and mnesia. The issue is that my mnesia schema for some tables was using a user provided name for the primary key. This was fine while it was just a prototype but now it was time for it to grow up. The record describing the table had to change and all the records in a table had to change. Now no one is really using this right now so I had a choice.
- Blow away the database and rebuild it.
- Be all erlang'y and stuff and do it live.
Wait a minute did he just say "live"? You can't update the schema and convert all the records live. That's crazy! Ahhh but this is erlang we do things different here. Witness the glory of iterate's db_migrate_tools:
Notice, the transform_stories function. It defines an anonymous fun it uses to transform the mnesia table with. Currently this function only has one signature. There is no reason it can't have more thoug. Since, erlang allows multiple signatures for anonymous functions, I can specify a signature for the next version of the mnesia table if/when it changes again. Here's the epiphany part. I can keep updating that fun with more signatures for each version of the table. Theoretically ,if I were to end up with a table that had records of multiple different historical types in it, I would be able to use this one transform function to get them all updated to the new record type. And I can do this all live without taking down the database or app. I can ship each iterate version with a module capable of updating the database live from any previous version. Now that's power that's useful. Try doing that with another platform.
Tags:
Published On: 2009-04-11 22:08:25
Iterate! is my Scrum style project management tool. Inspired by my dislike
for XPlanners UI. Iterate! was started about a month ago and is coded in
erlang using
mochiweb, and
nitrogen. Kick the tires and whatnot if you
want to see it in action: http://iterate.marzhillstudios.com:8001/
Tags:
Published On: 2009-04-11 09:59:12
When I'm in Perl, Javascript, Java, or any other programming language I prefer to throw exceptions rather than return errors. This is because I've long since gotten tired of losing errors 5 calls deep in the code because I forgot to check the return and handle it. Nothing is more annoying than trying to debug a problem that is actually caused somewhere else, but you don't know because the error vanished into the that Great Heap in the Sky around about 5 calls down. You've been there before. That's when you start adding print statements followed by an exit or, if your lucky enough to have one, firing up the debugger and using break statements to narrow down the actual source of the problem. This process could take days depending on how far removed from the breaking code the actual problem is. In erlang, however, returning errors actually turns out to be useful. This is because in erlang returning errors actually has the desired effect. Usually, when you return errors in an app, it is because you don't want to kill the whole program when something breaks. What you intend is for the caller to inspect the return and do the appropriate thing. This however runs smack into the whole
programmers are fallible people, who sometimes stay up late coding at 3am, when they can hardly see the screen anymore
problem. In short you are depending on the caller to honor your contract and do the right thing even though we often do exactly the wrong thing. Even when you are the caller, sometimes you don't honor that contract. Then a month later you are doing that print statement and die or perhaps the debugger dance again. All of this because of one night when your judgement lapsed. Companies will often develop elaborate style guides, testing strategy, and code-review cultures to prevent this, but in the end most developers I know come to love throwing exceptions... unless they are coding in erlang. This is because of two elements of the erlang language design:
- Pattern Matching
- and Fail Fast.
In erlang returning errors
requires the caller to handle them. If I try to store the return of a function and it doesn't match what I told it to expect then
bang, an automatic exception. However, if I try to store the return and explicitely handle the error case then no exception is thrown. This has the wonderful effect of forcing me to think about exactly what I expect this function to return, and handling or ignoring at my choice. The big benefit is that I had to think about it. The below example illustrates the difference.
The combination of Pattern Matching and Fail Fast in erlang forces the programmer to honor the contract, whether he wants to or not. This is one case where Erlang follows the "Do What I Need Not What I Want" principle in a language properly.
Tags:
Published On: 2009-04-10 13:47:53
I've been on
twitter for a while now andam currently up to 1789 posts. I'm a snippet twitterer. I typically don't try to connect tweets to previous tweets, but I'm fascinated by the people who do.
Brent Spiner for instance is at this moment twittering a saga of two women in chunks of 140 characters or less. I'm actually somewhat on the edge of my seat. Which is pretty decent for twittering. Perhaps I should consider doing some saga's of my own. First though I'll need to have something saga worthy happen to me.
Tags:
Published On: 2009-04-05 20:29:18
Hi there! Honestly I'm not dead, I've just been all wrapped up in this whole life thing. You know... that whole, "Holy cow!!!! I work at Google now!! When did this happen exactly?" thing, where you're incredibly busy just trying to get up to speed on it all and catch your breath? Well anyway, I feel bad since I've been silent for so long, so here goes. An update from the trenches of the Life of Jeremy Wall. Since I wrote last I have
- been "acquired by Google"
- had my first "truly successful" Open Source Project
- and actually got my Student Loan back under control.
I'm actually absurdly proud of that last one... And still a bit bewildered by the first one. So lets go down the list one at a time shall we?
"The Acquisition"
The company I worked for DoubleClick Performics got bought by Google. Who would have thought it? Somehow I landed an actual job at Google doing what I love. Crafting Code. I have to say, I think this has to have been a "God thing". I can't see any other way to explain it. Google of course is an awesome place to work. Free food, Smart people, Gameroom (strangely I almost never make into there though), Snacks, and really interesting technology to play with. I'm learning a lot about working in Highly Available, Highly Scalable environments. I keep waiting to wake up and find out it was all a dream.
"The Open Source Project"
My last post was about etap, my learn erlang project. I had no idea that the project would get the attention of Nick, a coder with EA, who was looking for a TAP compliant testing framework for their erlang code. He contacted me and asked if he could take over management of the code. I said" sure", as long as I still got to contribute when I had time. Before I knew it EA was using etap internally and I had what I consider to be my first truly successful open source project. etap has now gotten used commercially, traveled to conferences, and is soon to be featured in a book. Not bad for a learners project huh?
"The School Loan"
And perhaps most awesome of all I'm making headway on the whole credit repair thing. The school loan is back under control and no longer has a devestating impact on my credit report. This is an accomplishment that makes me really wonder if I'm in some kind of awesome dream or something. Life is really looking up around here. I'll try to get more regular on my posting again as I play with more erlang, think about writing a book, and Oh almost forgot to mention "Joose" the meta-object protocol for javascript. I'll write more about that later.
Tags:
Published On: 2009-04-05 19:32:16
foo
Tags:
Published On: 2007-09-22 05:28:23
Currently there is no simple way to test Moose::Roles. Since they defer things like attribute adding and method wrapping you have to create a dummy class that uses them to test what they do. Usually this is through creating a package inline in the test module that does what you need or don't need based on what your testing. Therefore I'm considering either adding Role support to Test::Moose or creating a Test::Moose::MockObject module to make this easier. Still trying to decide which way to go. Maybe I'll go both ways :-)
Tags:
Published On: 2007-09-17 03:21:21
I have taken on the task of learning erlang. I was trying to decide between
learninng Haskell, OCaml, or Erlang. OCaml, I decided against since it had too
close a similarity to C and I wanted to really stretch myself.
Haskell and Erlang both fit that bill however I found the Erlang
Documentation to be far better for someone completely new to the functional
programming world. Haskell's idea of a tutorial tried to cover too many
concepts at once and took too long to get to the hands on stuff. Also erlang
offered the opportunity to learn Distributed programming concepts along the way
so erlang it was. You can see my first erlang project
etap here.
Tags:
Published On: 2007-08-27 16:43:45
So I've added two new modules to my CPAN repertoire. Data::Annotated and Class::Data::Annotated. Data::Annotated is a module intended to hold an annotation about a piece of a data structure independently of the Data::Structure itself. The annotation can be anything a hash an array or a scalar value. The piece of the data structure is referenced by a Data::Path. Class::Data::Annotated wraps a perl data structure and an associated set of annotations together in one place. I've also added to Data::Path's functionality so that it can annotate object methods and coderefs stored in a data structure. Once I've ironed out details with the original author I'll hopefully be uploading that. Anyway feel free to check them out:
My CPAN Libraries
Tags:
Published On: 2007-08-14 16:20:30
And I have registered its namespace so it shows up in the module list. This means the Bricklayer::* namespace can now be used to begin build the various components of my evolving frameworks
Bricklayer::Templater
Tags:
Published On: 2007-07-14 19:35:19
I haven't seriously looked at PHP in years having long ago joined the Perl camp. I know I know shame on me. However from that very perspective it has seemed to me that php's biggest problem is the addons. Yes Pear and some of the other frameworks alleviate it somewhat but really PHP looks and acts like a thrown together language. Just look at the function list in the documentation. The various libraries it uses are binary addons . Their is little consistency in useage, naming conventions and almost no namespacing used in it. I'll quote
Jeroen van der Meer:
PHP is basically a collection of extensions which are all put together to form what we have now. However, these extensions change and so does the collection.
This is almost completely different from the way every other language does it. You might be able to make the case that Java is like this too but even java uses namespacing and most of the libraries are written in java itself. In a way PHP's worst problem has been it's community of users and the language maintainers enablement of that community. The perl community may seem a bit gruff to a php user but getting a module featured in CPAN takes a little bit more work than it seems getting an addon into the official php distribution took. This actually fosters the bad coding practice most perl and ruby users associate with php coders. PHP
may finally be growing up but it's like taking a rebellious child who hasn't been disciplined in years and sending him to bootcamp. He'll have to be dragged kicking and screaming obscenities but hopefully the result will be worth it.
Tags:
Published On: 2007-07-14 19:16:38
Yay I'm finally getting around to it and boy is it a good thing. The code is already benefitting from it. I'll be uploading some stuff to the sourceforge svn repository soon so stay tuned.
Tags:
Published On: 2007-07-10 04:56:19
So I'm finally starting to get settled at the new job and I thought I'd let folks know how things are going. I'm liking the SCRUM Process for development and the AGILE emphasis here. I'm also finding a surprising number of ways to contribute. So far I've been useful in helping with resolving SVN branching conflicts. Developing an XML Schema. And Javascript Library standardization recommendations. In fact I may be doing some roundtable Dev learning sessions in the next month or so on some of those topics for the other devs which should be fun. I have some pending articles on here to finish up and some features to release for Bricklayer and Metabase when I get some time to do so. Which means this space should soon be experiencing some more activity.
Tags:
Published On: 2007-03-23 21:40:31
I'm in the market for a laptop. I'm either going to be buying a refurbished Mac. Or I'm going to buy a laptop certified for linux. What I want in a laptop if I get a linux one is: already installed and configured wifi. Nvidia graphics chipset with dedicated memory at least a dvd drive dvdrw preferred at least 512mb of memor 1gb preferred at least 80 gb hdd 100 perferred 10/100 ethernet port If anyone has recommendations or even one to sell let me know. Addendum: I have purchased the laptop. I bought a sony Viao VGN AR320E.
Tags:
Published On: 2007-03-20 15:57:54
I have found a great new tool in CPAN.
Moose is an extension of the perl OO system. It implements metaclasses in an intuitive and easy to understand way. Getting started with Moose couldn't be easier. Make sure you use v0.18 though because the previous version has a few quirks that get in the way. Basically to get started all you need to get started is install Moose from CPAN. I recommend doing so from CPAN because a lot of the distros are behind a little and 0.18 has some fixes that will make your life a great deal easier when using Moose. Once you've installed Moose it's time to start building your classes. In this tutorial I'm going to highlight the most used (in my opinion) features of Moose.
A class is composed of attributes and operations for the most part. Some OO systems further divide these into private and public methods/attributes. For this tutorial though we will just keep it at those two. The first thing you have to do in your class is use Moose at the top to import the Moose package.
package MyClass; use Moose;
Now your commited. That use Moose statement has forever altered the way you write this class. Well not really you can turn it off but lets not worry about that right now. Moose does a lot of heavy lifting for you in the background so it's best to just use the moose feature set to build your class from here on out. The first thing we need to do is start writing your classes attributes. For this we will need our handy dandy
'has' function.
'has' will create our attributes for us and automatically do type restriction, create accessor methods, and police reading and writing to them. So what does
'has' need? It needs two arguments a scalar and a list of key value pairs with a minimum of at least one but I recommend 2 keys at a minimum. The scalar is the name of the attribute. The list describes the attribute for Moose so it knows how to set it up for you. The first key, and the absolute must, is the
'isa' key. This key tells Moose what type to restrict the attribute to. It can be a class name or predefined subtype. You can see a list of Moose's of already defined subtypes for us
Here : http://search.cpan.org/~stevan/Moose/lib/Moose/Util/TypeConstraints.pm#Default_Type_Constraints You should be able to use those to get started. The second key Moose needs is the
'is' key. This key tells Moose how to police reading and writing to this attribute. A value of 'rw' in this key tells Moose this attribute can be read and written. A value of 'ro' in the key tells Moose this attribute can only be read and not written to. So lets add a couple of attributes to this class.
has 'Name' => (is => 'rw', isa => 'Str'); has 'Purpose' => (is => 'ro', isa => 'Str');
Now our class instances can have a name and a purpose. We can see that both attributes are strings and that the name is readable and writable while the purpose is only readable. This is actually a fully functional class now. It only works to store things since we don't have any operations yet but it is fully useable. A few things to keep in mind is that Moose automatically adds Moose::Object as your classes base class. So you do have a few operations: ->new() is a constructor that Moose::Object provides for you. This allows Moose to properly set up your classes accessors and constraints for you. We could use this class now by calling
my $obj = MyClass->new();
We can set our name by calling
$obj->Name('test');
We can retrieve that name by calling
my $name = $obj->Name();
But wait our Purpose attribute is not writable and nothing is stored in it. How on earth do we get something in there? Ahhh, now we come to Mooses little secret:
You don't have to use the methods to access those attributes.
$obj->{Purpose} = 'To Show off Moose'; will work just as well with a few caveats. (You see it really is just an extension of the Perl OO system.) Moose doesn't do any policing when you access them this way. This means people who use your class and regard it properly as a black box shouldn't be doing this. Which brings us to two more really handy keys in our descriptive list we are passing to has: 'default' and 'required'. Up to now our attributes have not been restricted from being undefined. setting the required => 1 key/value pair in our list takes care of that. If we do that though then we have to define a default value for the attribute or our class will error on compile with Moose telling us that the attribute is undefined. That's what the default key is for.
default => 'To Show off Moose' will set the default value for the attribute to our string. Now our class looks like this:
package MyClass; use Moose; has 'Name' => (is => 'rw', isa => 'Str'); has 'Purpose' => (is => 'ro', isa => 'Str', default => 'To Show off Moose', required => 1);
When we create a new instance of our class with
my $obj = MyClass->new();
our Purpose attribute will be preset for us and not modifiable (without breaking the rules which you would never do of course). That's enough for this post. I'll be posting next on the benefits of after and subtype when it comes to your attributes and Object Integrity.
Tags:
Published On: 2007-03-12 23:00:55
Blender is known as the most complete Open Source 3D modeller out there for open source. It also has a reputation for being one of those love it or hate it software packages with a steep learning curve. Blender is more than just a 3d content creation package though. It also happens to be perhaps the best video compositing and Non Linear editor available for open source. Cinelerra has a lot of power but isn't particularly great when you need to do a lot of keyframing. Jahshaka has a lot of potential but is unstable and still has a long way to go. Kino only does Digital video. But blender?.. Blender has it all. Blender is quite possibly the only package that gives you an end to end solution for content creation. What can blender do for you as a video editor? Well Just about anything actually. In can composite images and video/animations. It has a non linear video editor. It has an audio seqencer. In just under an hour I did the following short video clip using three still images.
Compositing Still Image Test Video Blender has introduced node based compositing and as of 2.43 it has become quite powerful in that arena. The .blend file to do the effects seen above can be downloaded here. Hopefully it will show you some of the power that blender can bring to a video production pipeline.
Tags:
Published On: 2007-02-28 13:02:04
There is an axiom coined I believe by Sir Arthur Conan Doyle in his Sherlock Holmes novels that every programmer should keep in mind while debugging his program.
When you have eliminated every other possibility; Whatever is left, however improbable, Must be the Solution.
There has been many a time when I could have arrived at the answer much sooner but was stuck because what the program was doing seemed to be impossible. When I accepted that it was possible then I was able to begin tracking down how it was possible and thus finding the solution. After one too many occurences of this I think I'm going to make a big poster with this axiom on it and hang it above my monitor.
Tags:
Published On: 2007-02-22 16:37:57
I'm not sure how useful this actually is but you can't deny that it's pretty cool. May I present to you the Cube:

Now, like all the things Beryl can do, this is in realtime so you can watch progress bars move, text scroll, and movies play all while rotating your cube.
Tags:
Published On: 2007-02-22 16:36:50
Desktop Cube with 3d window effects
Tags:
Published On: 2007-02-15 15:32:10
A particularly handy feature of Beryl is the Scale Plugin. This plugin gives an easy intuitive way to select the window you want to switch to on a busy desktop. It can be activated by setting a hotspot or using a hotkey. It takes all the open windows and tiles them in the screen scaling them to fit so you can pick the window you want to switch to easily. It also displays those windows in realtime so video keeps playing commandline console text keeps scrolling and so on. Here is a screenshot of this feature:
Tags:
Published On: 2007-02-15 15:31:00
The Beryl Scale window selection plugin
Tags:
Published On: 2007-02-14 13:48:20
Beryl allows you to do realtime transparency and thumbnailing with windows. The following screenshot demonstrates these capabilities. There is a video playing underneath a transparent window and also playing on the thumbnail of the window over the taskbar. Unfortunately you can not see the video actually playing in the thumbnail and underneath the transparent window so you'll have to take my word for it. Its a powerful demonstration of how powerful the Beryl/XGL architecture can be.
Tags:
Published On: 2007-02-14 13:47:43
Video playing through transparency and in a thumbnail
Tags:
Published On: 2007-02-13 12:48:52
Beryl has a feature called Grouping and Tabbing. It's a way to organize windows on your desktop to save space. And as promised I have screenshots demonstrating the feature. This first one shows the tabbed group with the tab selection popup.

You can choose your animation when switching tabs just like anything else in Beryl. Here is a screenshot of the rotation animation when switching tabs.

Stay tuned for more screenshots as I identify features that I think are worth highlighting. Next will be some transparency examples and window thumbnailing.
Tags:
Published On: 2007-02-13 12:43:56
Switching Windows in a Tabbed Group in Beryl
Tags:
Published On: 2007-02-03 01:22:16
Thanks to the wonders of open source. I now have a fully OpenGL Accelerated Desktop. Complete with realtime window opacity, An Expose like interface a totally awesome task switcher, wobbling windows, Window animations. But the totally blow me away feature was window thumbnailing. if I have a video running and minimize the window then hover over it in the task bar then the thumbnail shows the running video!! I'm like a kid in a candy store. Now most people would be like sure who cares but the task switcher and Expose like feature are incredibly useful. Beryl and XGL Rock!!! Screenshots to come.
Tags:
Published On: 2007-01-25 19:34:17
There is a CPAN module
Class::Struct that can give you this same functionality. But fool that I am I like to do things the hard way. Now the differences in the useage of this implementation, while it may not do things as automatically as Class Struct does, will allow you to create simple type restricted attributes on the fly in your code with a simple one line class method. You could even bundle this in with an AUTOLOAD function to build the attributes as you need them. Also the class attributes are added at runtime and with a little extra work you can even specify such things as typed arrays or hashes. Ok enough Pro's and Cons lets take a look at the code. First we take a look at our base class that does most of the work.
package Class::Builder; sub new { my $class = ref($_[0]) || $_[0]; my $self = {}; return bless($self, $class); } sub attribute { my $self = $_[0]; my $type = $_[1]; my $attribute = $_[2]; my $value = $_[3]; if ($value) { #handle INT case if ($type eq "INT") { if ($value =~ /^[0-9]+$/) { $_[0]->{$attribute} = $_[3]; return $_[0]->{$attribute}; } else { $_[0]->err("Not a $type value for $attribute"); return undef; } } elsif ($type eq "SCALAR") { #handle simple SCALAR case $_[0]->{$attribute} = $_[3]; return $_[0]->{$attribute}; } elsif (ref($value) eq $type) { #handle other types $_[0]->{$attribute} = $_[3]; return $_[0]->{$attribute}; } else { $_[0]->err("Not a $type value for $attribute"); return undef; } } else { $_[0]->err("No value passed for $attribute in ".ref($_[0])); } return $_[0]->{$attribute}; } sub err { $_[0]->{err} = $_[1] if $_[1]; return $_[0]->{err}; } return 1; Now lets see how we can use it.
package Document; use Class::Builder; use Document::Section; use base qw(Class::Builder); #Attribute Methods # example of a SCALAR Typed Attribute implementation sub Name { return $_[0]->attribute('SCALAR', 'Name', $_[1]); } #Example of a ARRAY Typed Attribute with a further simple check #that the array elements are of type: Document::Section sub Sections { my $arraytype = 'Document::Section'; my $sections_old = $_[0]->Sections(); my $sections = $_[0]->attribute('ARRAY', 'Sections', $_[1]); foreach (@$_[0]) { if (ref($_) ne $arraytype) { #throuw an error here $_[0]->err("Invalid Array Element $arraytype"); $_[0]->attribute('ARRAY', 'Sections', $sections_old); # reset the Sections array return undef; } } # die "sections is a ".ref($sections); return $sections; } # example of a HASH typed Attribute sub Meta { return $_[0]->attribute('HASH', 'Meta', $_[1]); } # example of an INT typed Attribute; sub Cursor { return $_[0]->attribute('INT', 'Cursor', $_[1]); } I'm not finished modifying this concept so I may post some additional enhancments later. But you can get the idea now.
Tags:
Published On: 2006-12-14 00:36:30
So contact me for a job now before I become full time employed again. For that matter feel free to contact me about full time employment too.
Tags:
Published On: 2006-12-02 19:09:34
Just Julie popping in to give a big "HI!" I thought it'd been a while since there was a post here, so here is a little one.
Tags:
Published On: 2006-08-30 21:33:24
The subject that drives the most interesting traffic to this site is Apache2 with mod_perl2 questions. To that end after I finish this major software project on a mod_perl platform I intend to write up a more detailed tutorial on actually using mod_perl2 in a production environment and what it can do for you. So watch this space :-) I'll be done with the project before too long.
Tags:
Published On: 2006-08-24 14:53:30
As long as I've been doing stuff on the web I've heard the constant refrain about semantic markup.
Here and then
here are just two of the latest. So I thought I'd add my own thoughts to the mix. What often gets lost in these discussions is the difference between semantic markup and the display of your markup. The fellow over at
Six27 makes the point that using CSS obviates any need for semantic markup as it pertains to the browser's display. He seems to think this is what makes semantic markup meaningless. However that is exactly the point of semantic markup. You can structure the data on the page any way you wish and have the presentation however you want it. This actually makes semantic markup easier. Semantic markup is not there to help you display the data. It is only there to make more information available to those who want to use it. He also makes a point that lack of universal browser support makes semantic markup useless. That would be true, if universal support was necessary to make markup useful. In point of fact, universal support is not necessary. What is necessary is a standard. You don't really care if every client supports the standard. What you do care about is if there is a standard that allows the people who wish to to take advantage of your semantic markup do so. In the end what is necessary is just support of the standard by your intended audience. The semantic markup paradigm isn't dead it's just being used under the surface by those who have a use for it. And thanks to CSS everyone else is free to ignore it if they so choose.
Tags:
Published On: 2006-08-22 12:23:49
I've revamped and rewritten the Bricklayer documentation to reflect some significant changes to the API. You can get it off the
Sourceforge site or
browse it online here using the link on the menu. Please let me know if you find any errors in spelling grammar or the API description. Also if there is any additional information you might want included in it.
Tags:
Published On: 2006-07-27 21:26:31
SourceForge project page Have a look if you want.
Tags:
Published On: 2006-06-22 15:17:14
If you have always wanted the functionality of GNU Screen for your win32 box but couldn't get the win32 port to do the detach/reattach portion correctly then your not alone. And thanks to the power of open source
some guy created a stripped down version called dtach that doesn't have all the fancy stuff. This version just so happens to compile and run perfectly on cygwin. It might even do it correctly on just a mingw32 setup. What this means is that you can now run detached terminal sessions on your windows box. I've provided the binary for you to use if you're interested.
dtach Example shortcut to use it
Tags:
Published On: 2006-06-22 15:15:34
Shortcut that launches dtach and fires a shell to use in it
Tags:
Published On: 2006-06-22 15:14:19
the stripped down win32 GNU Screen binary for Cygwin
Tags:
Published On: 2006-05-11 11:24:43
My next mod_perl article is up:
Mod_Perl 2.0 Writing a Useful Handler Read it while it's hot.
Tags:
Published On: 2006-05-11 11:15:09
In the last article we looked at all the tools mod_perl gives us when using it. Now lets take a look at some of the things you can do with those tools. I will be demonstrating what use those tools can be in a real world app. The first thing to do is create our skeleton handler for the app. You can use the handler we put together in the previous article for this. Just replace the contents of the handler subroutine with the code we will be generating here. I'm using my
bricklayer framework to demonstrate this but the lessons apply equally well to most other applications. The first thing we need to do is load any libraries we might need. In my case this is just the bricklayer module.
package mod_perl_wrapper; use strict; use warnings FATAL => 'all'; no warnings 'redefine'; use lib "/data/Jeremy/workspace/BrickLayer_Main"; #Apache2 Mod_Perl2 libraries use Apache2::RequestRec (); use Apache2::RequestIO (); use Apache2::Filter (); use Apache2::Const -compile => qw(DECLINED OK); #the apache2 constants we need # Bricklayer Library use BrickLayer (); #my bricklayer module sub handler { } 1;
There a several things to note about the code above. First is the use lib. mod_perl looks in the usual places for your libraries. You might think that just a simple . in the @INC array will make it look in the same location as your handler. However in a mod_perl environment ./ actually refers to the Apache root directory. Therefore it's always a good idea to explicitely tell it any nonstandard library locations in your handler. Also note the mod_perl2 libraries I loaded. These are the most common libraries your app will need so I included them here. Now lets get started building that app shall we? Our handler has several jobs to do.
- examine the URL string to see what page was requested
- examine and request variables and modify them appropriately
- retrieve any post information and send that to the appliaction or provide a way for that application to retrieve it
- run the application
To this end we need to know the path_info(), the args(), the contents of read() and be able to pass that information to our application. So lets take a look at accomplishing those tasks now. First we need to see what page was requested. We can get this information from the path_info method of our Apache2::RequestRec object. Once we have retrieved it we can parse out the needed information. In my case I want to look for *.txml files and turn those into a page request in the request string. This will allow me to hit my template files directly without anyone knowing or caring if I use a perl script to parse them first. It makes my URL's friendlier and helps in site tracking. To that end I need to take any .txml files requested and prepend or append them to my request string. Here is an example of just that.
#$_[0] is our Apache2::RequestRec object my $pathinfo = ""; $pathinfo .= $_[0]->path_info(); return Apache2::Const::DECLINED unless ($pathinfo =~ m/(\.txml$)|(\/$)/g) or ($pathinfo eq ""); $pathinfo =~ s/^\///; $pathinfo =~ s/\//::/; $pathinfo =~ s/.txml//; #rewrite the request string; $_[0]->args("Page=$pathinfo\&".$_[0]->args()) unless $pathinfo eq ""; #
First we retrieve the pathinfo string and test to make sure it contains a *.txml file at the end. We don't care for this example if there are any at the beginning so we will consider anything else to invalid for us to handle. If there is no .txml at the end then we decline to handle this request and let apache take over. by returning the Apache2::Const::DECLINED constant. If there was a *.txml file requested though then we want to capture that and modify it for use in the bricklayer app. I use a series of substitution expressions to rewrite the path string stripping off the beginning slash and changing all other slashes to "::" so that bricklayer can understand the template request. Once I have rewritten the string I prepend it to the argument list as a Page argument using the args() method. This method both retrieves and lets us set the argument string. One nifty thing about mod_perl environments is that you can freely choose to ignore whether or not you are processing a POST or GET request. This means you can post a form to a request string and still retrieve the request string variables. Something that would have been difficult to do in traditional CGI environment. It also means that when I translate this path request I don't have to worry about whether this was GET or POST request since my cgi wrapper can process both simultaneously. That's really all my handler needs to do now it just calls the bricklayer app and runs it with the new request string. We aren't finished yet though. The next article will cover how I created a CGI wrapper and rewrote CGI_Lite.pm to handle mod_perl requests.
Tags:
Published On: 2006-05-03 10:40:31
Now don't get me wrong. I really like the wordpress UI for posting and managing pages and posts and comments and spam. However they are sadly sadly lacking in the templating department. I have discovered this in the last few days after attempting to modify a template. I really dislike the way it all works. You aren't templating your coding in PHP. and it's a little obtuse that way. It's probably just a matter of preference. Perhaps I should right a templating plugin. If that's possible. I wonder.....?
Tags:
Published On: 2006-05-01 15:15:47
ABlogApart a collection of folks with thoughtful commentary (or commentary at least). Is up and running again after a long hiatus. Drop by and meet the folks if you feel like it.
Tags:
Published On: 2006-04-22 20:33:50
Here is my collection of Mod_Perl 2.0 tutorials and notes.
Tags:
Published On: 2006-04-22 20:32:48
Right now there is a shortage of really easy to understand documentation on using mod_perl to write web applications. There are a lot of examples on using it to rewrite URI's, redirect output, run CGI apps unaltered and even turning Apache into an email over http protocol server. Those are all really wonderful uses, but I want to build a website. So how do you go about doing that? Especially if you aren't using CGI.pm on the backend. This article and others after will focus on that topic. The tutorial is for mod_perl 2 on Apache2. It works equally well on windows or Linux as far as I can tell. I shall be assuming you already have or can find out how to get mod_perl 2 and apache2 on your server. Basically you will be following me as I peek into the internals of making mod_perl useful. Lets start with a simple script to take a look at the internals of what mod_perl lets you do. Here is our script:
package mod_perl_report; use strict; use warnings; use Apache2::RequestRec (); use Apache2::RequestIO (); use Apache2::Const -compile => qw(OK); sub handler { my $r = shift; my $report; $r->content_type('text/plain'); $report .= "server: ".$r->server()."\\n\\n"; $report .= "hostname: ".$r->hostname()."\\n"; $report .= "user: ".$r->user()."\\n"; $report .= "unparsed uri: ".$r->unparsed_uri()."\\n"; $report .= "uri: ".$r->uri()."\\n"; $report .= "filename: ".$r->filename()."\\n"; $report .= "pathinfo: ".$r->path_info()."\\n"; $report .= "request time: ".$r->request_time()."\\n"; $report .= "request method: ".$r->method()."\\n"; $report .= "request string: ".$r->args()."\\n\\n"; $report .= "cookies: ".$r->headers_in->{Cookie}."\n"; $report .= "status: ".$r->status()."\\n"; $report .= "status line: ".$r->status_line()."\\n"; $report .= "notes: ".$r->notes()."\\n"; $report .= "\n\npost data: ->|".read_post($r)."|< -"; # $report .= "\n\nmodifying variables now: \\n\\n"; print "mod_perl 2.0 Debugging output:\\n\\n"; print $report; return Apache2::Const::OK; } sub read_post { my $r = shift; my $buffer; my $data; while ($r->read($buffer, 1000)) { $data .= $buffer; } return $data; } 1;
You will need to save it into a file called mod_perl_report.pm and then tell mod_perl where it is. To do that you will need some configuration directives in your apache2 httpd.conf file. Now in my case I created a folder in my apache server's root directory (whatever you set ServerRoot to in the conf file) called mod_perl. Then I created a file called mod_perl_prep.pl in the apache configuration directory with my common use statements. In this file I also had the line
use lib qw(mod_perl);
so that mod_perl will know where my handler is. I call the mod_perl_prep.pl script from the apache configuration file like so:
PerlRequire conf/mod_perl_prep.pl
and finally since this is a development machine I save myself some time by adding this line directly underneath:
# comment out the following line for production use. PerlInitHandler Apache2::Reload
That line tells mod_perl to reload any modules I use when they change so I don't have to keep restarting apache to see my changes. Believe me you will want this on your development machine cause the restarting gets really old really fast. Now we are ready to tell Apache when to call our debugging report handler. At the bottom of your configuration file add the following section:
SetHandler perl-script PerlResponseHandler mod_perl_report
Now Apache knows that when it sees the /report URI after the hostname it needs to call my mod_perl_report handler. So lets take a look at that handler right now and see what it does. This module illustrates all the most useful pieces of the Apache2::RequestRec object that I have so far been able to figure out. It starts out with our modules package declarations and any use statements we will need to do the work we are planning to do.
package mod_perl_report; use strict; use warnings; use Apache2::RequestRec (); use Apache2::RequestIO (); use Apache2::Const -compile => qw(OK);
This is all the code we will need in order to use the variouse Apache2 mod_perl interfaces for our web app. Now to be a full fledged working http request handler we need one last detail. All mod_perl handlers require a handler subroutine like so:
sub handler { my $r = shift; my $report; $r->content_type('text/plain'); #handler code goes here # print "mod_perl 2.0 Debugging output:nn"; print $report; return Apache2::Const::OK; }
There are several pieces to this handler that are important. First the name. It has to be called handler. If mod_perl can't find the handler sub in your module it can't use it. Second the my $r = shift; This is our mod_perl Apache2::RequestRec object for the request we are handling. Lastly the return statement. You will in almost all cases be returning one of two Apache2::Const constants. OK or DECLINED. OK tells mod_perl that your handler is accepting responsibility for this request. DECLINED tells mod_perl that your handler is declining responsibility for this request. You can import all of these constants and many more with the use Apache2::Const -compile ->qw(); statements. See the Apache2::Const documentation for a complete list. Now lets get down to the real nitty gritty. I wanted this script basically to be my "hello world" for mod_perl. It needed to demonstrate how to set up and use mod_perl for a real web app. To that end there were several things I needed it to do. I needed it to be called when I hit a particular url. I needed to output something my browser would understand. And finally I wanted it to be useful information. As I said before there is somewhat of a lack of truly useful information online for this kind of thing. For instance if I have a form that POST's data to a page how do I retrieve it. CGI.pm handles it for you of course. But I really like to know exactly what kind of environment I'm working in. Just chalk it up to that hacker spirit coming out. After paging through a great deal of
documentation on the perl website I finally narrowed it down to the pieces that looked the most useful.
- The Apache2::RequestRec object
- $r->hostname()
The Hostname our server is being called by. - $r->unparsed_uri()
The unparsed URI of our request. This is the whole shebang including any GET strings and path information - $r->uri()
The URI our handler is handling. This should correspond to whatever was in our location section of the httpd conf file. In this case /report - $r->filename()
This is the exact path our URI maps to on the server harddrive. - $r->path_info()
This is the portion of the URI that comes after our uri from above with the get string excluded if there was one - $r->request_time()
This is the timestamp for the request - $r->method()
This is the Request method it could be any one of GET, POST, PUT... and so on. Even one you made up if you have a client that can send it. And your handler takes over the request at the correct time. - $r->args()
This is the request string. Basically anything that appears after a question mark in the URI. If your handler was called at the right spot in Apache's request handling then you could potentially capture both GET and POST data sent from a client in the same request. I'll go into that more later. - $r->headers_in->{Cookie} This is the string from the cookie headers. headers_in basically gives you a hash like tied interface to the headers from the client.
- $r->status()
This is the current response code for the request (eg. 200, 404, 500). You can also set the response code here. - $r->status_line ?? undetermined yet. If you know more then leave a comment
- $r->notes() ?? undetermined yet. If you know more then leave a comment
- $r->read($buffer, $len) read from the request buffer. Used to retrieve any posted data. Use is much like perls core read() function. It is used in our read_post subroutine to read any post data that might be present.
You can hit /report on the server with your browser to see exactly what these methods actually return. Use an html form that posts to this page to see how that works too. That's probably enough to cover in this post. In future posts I will talk about using a CGI module like CGI_Lite in this environment and some of the neat tricks mod_perl lets you do that no other web development environment allows.
Tags:
Published On: 2006-04-18 15:09:00
I bit the bullet and upgraded finally. And might I say the upgrade was a snap. Not a single problem while doing so. I am impressed.
Tags:
Published On: 2006-04-12 18:52:10
http://www.opinionjournal.com/extra/?id=110008220 trust me it'll open your eyes on a few things. and yes I know this has absolutely nothing to do with code or even computers but I though it was important.
Tags:
Published On: 2006-04-06 09:09:26
Bill Gates the man behind Microsoft. Has published an interview with CNN on how he works. At the article:
Bill Gates - How I work on money.cnn.com. Some of it no doubt is more advertising than informative however it is nice to see someone who actually uses what his company produces. The problem with Microsoft nowadays isn't the quality of their products. It's the price and the licensing. Microsoft has some great integrations in their products. If you can afford to purchase the sharepoint servers, Latest Office version, all the little office addons, and keep em up to date then by all means your office can experience a lot of internal productivity. The thing about Bill's job though is that he doesn't have to worry about incompatibilities within his organization or when dealing with other companies. Microsoft always has the latest versions of everything internally. Bill works in sort of an ivory tower where everything just works because they write the software. Out there in the real world though Microsoft is losing a very important battle. They are still fighting to keep things a Microsoft world. But the Genie is out of the bottle and eventually they will have to begin supporting open standards with no catches. People will stop caring about the nifty new feature if they can't use them anywhere they want on any platform. Microsoft in point of fact can't depend on everyone's work place being like microsofts anymore. I wonder what happens when Bill gets an email back saying: "Im sorry but we can't read your office 200x document please resend it in an open format like ODF, Plain Text, or RTF. Thank you." That day is coming we may not know when and I'm not stupid enough to try to predict it. but it is coming. The free market is running like it's supposed too and Microsoft is in for a rude awakening if they don't start preparing for it. Keep an eye on Minnesota. I have a feeling this trend we are seeing may accelerate exponentially.
Tags:
Published On: 2006-04-05 10:54:02
But I have clients on Network Solutions and they must have just about the worst service I've ever seen for DNS. and to top it off their servers go down way to often for the price they charge.
Case In Point That was just the latest outage. So if any of you are thinking of getting DNS or Domain Names from them. Don't. Just a warning but I really haven't been impressed by them.
Tags:
Published On: 2006-04-03 21:40:04
Have you ever wished your CMS seamlessly handled multiple types of content and multiple ways of organizing, storing, and presenting them? Well so have I. So now after 2 years of working on Bricklayer off and on I'm finally going to build something useful with it. I'm a man with many interests and I'd like to store and share them all. MetaBase will be designed with that in mind. Whether it's an image, essay, diary entry, or tutorial or howto, MetaBase will handle it and display it in an appropriate way. It will also allow me to manage it using such exciting technologies as metadata, tagging, and hiearchical ranking. This should be fun.
Tags:
Published On: 2006-03-27 11:23:04
In programming we have the term Easter Egg. It refers to hidden functionality in an application. Many times hidden even from the management. Some of you may remember the famous pinball game or Flight simulator hidden in the Office 97 Apps. Games are another popular place for programmers to hide Easter Eggs. They are kind of the Programmers little joke for the User of their software. Easter Eggs are fun and can really brighten a dull day. Some of you may know that I consider programming a creative art. In fact I consider some programming to be not unlike painting or writing poetry. For those who think like that, Easter eggs are like those hidden messages artists will sometimes hide in a painting. If you know me very well you will also know I'm a christian. I believe our capacity to create and enjoy beauty and surprise are a trait that comes from being made in Gods Image. When you think about it Creating the Universe was kind of like programming a work of art. God is the ultimate Hacker. He encoded the biology of whole Species into a few strands of DNA. He wrote the rules of Physics, and designed how Numbers work. His code is so beautiful it would move Linus Torvalds himself to tears. And like many coders I think God added a few Easter Eggs to his work. So the next time you hear of some funny coincidence that just strikes you as a little odd and somehow funny just imagine God in the beginning of Time turning to the Holy Spirit and saying "Wait till they get a load of this one..."
Tags:
Published On: 2006-02-28 10:30:51
Tags:
Published On: 2006-02-21 10:08:57
I'd like to take a moment to wax poetic. Code hackers have a term for well written code. Elegant. We appreciate elegant design and algorithms in code. It's a pleasure to work on code like that. We will stop and just think "Man, that's beautiful" Even our quick and dirty scripts somehow turn out to be pieces of art. So what makes a piece of code beautiful? It's a little bit hard to describe but there are usually several elements that contribute to code's beauty. Those elements that compose what I perceive as beauty in code are:
- Efficiency
- Cleverness
- Style
- Flow
Efficiency Efficiency is perhaps one of the most important elements in the percieved beauty of code. Code that is streamlined, sleek, and targetted is beautiful. This kind of code does one thing and does it well. There is no wasted effort or duplicated work in efficient code. Efficient code knows what it needs to do and gets down to business. Many times this equates to less code though not always.
Cleverness Cleverness is a close second in the elements of code beauty list. Cleverness as defined by "Now, that's a
cool way to do it!!" It's coming up with a better and heretofore unconsidered method or algorithm to get the job done. It's similar to those paintings in art with surprise built into them. Like the excercises in perspective where you don't realize it's a painting till you get close. It makes you stop and go Wow! Now that's cool. Code cleverness usually has a lot to do with Efficiency. If your clever hack makes the code less efficient it may actually decrease your codes beauty. It can be a double edged sword.
Style Style along with Flow are the subjective parts of the beauty equation. It means different things to different people. It's part of what makes someone love perl and hate python while a different person loves python and hates perl. Style encompasses such things Indenting, code organization, and naming conventions. Everyone has a different opinion of what looks good. Similar to art where one person likes modern art and another thinks it looks ridiculous.
Flow Flow is also a highly subjective part of the beauty equation. Some people like to flowchart for days before even touching the keyboard. Others prefer to let the programs logic structure sort of organically grow. Still others prefer a balance somewhere between the two. Flow covers how your code handles the various tasks that it is responsible for. It encompasses reusability. And it can increase or decrease your code's efficiency. Like Art everyone has their own definition of what is beauty when it comes to Flow. So how do you classify beautiful code? Let me know in the comments.
Tags:
Published On: 2006-02-17 10:30:50
One of the things Google does well is integration that makes sense. Take the new enhancements to Gmail And Google Talk. For everyone who thought that we didn't need another talk client May I humbly present: Gmail Talk beta. I knew they had something up their sleeves. And the integration is both seamless and easy. This is what makes Web Applications done right such a beautiful thing. I just log on one day and boom my wife can IM me an important message with no work on my part whatsoever.
- No client to install.
- No configuration option to set up.
- No service to sign into.
Now that is the way it should be. Invisible till I need it. Just instant communication from just about any web browser in the world. Now all I need is Google Calendar and my Google Experience is complete.
Tags:
Published On: 2006-02-13 23:30:23
SSL & Phishing get cozy, a look at how the system broke, is a very good analysis of a recent very targeted phishing attack. The important thing to remember here is just because one big company says it's so doesn't necessarily mean it is. That little lock that appears to tell you your using an encrypted connection doesn't always mean you connected to the site you thought you were connecting to. Always make sure you're familiar with you're financial institution's websites. And it wouldn't be a bad idea to verify emails by phone too. I can guarantee you those malicious phishers are quite capable of pulling the wool over your eyes the moment you let your guard down. Take it from me. Banks that email you stuff out of the blue are bad.
Tags:
Published On: 2006-02-08 23:55:44
BrickLayer RC2.1 Bugfix and modifications to the Bricklayer RC2 release. Accompanied by
amended documentation.
Tags:
Published On: 2006-02-03 11:36:11
If you've been floating around the net for any significant amount of time you've probably heard about
DRM. And if you like Open Source you've probably heard a little about the GPLV3 license brouhaha. No doubt you're even wondering what exactly it all means. Well
Linus sums it up pretty well. The key points here are that DRM is primarily about using Valuable Security Technologies in unintended ways. The same possibilities that make DRM useful help make systems more secure. The way to Fight DRM is not fighting the technology. It's protecting the content. If you dislike DRM then make sure your content can never be used in a DRM protected work. Protect your content don't fight the technology. Open Source is winning the Software licensing battle because it produces Quality Products under a less restrictive environment for use. Open Content can do the same. That's something the EFF seems to have forgotten. Lets hope the artists start paying attention before the barrier to entry becomes too high.
Tags:
Published On: 2006-01-30 10:27:57
I have finished moving my bricklayer subversion source code repository to a public location. you can find it at the following link. If your browser recognizes the svn protocol you can just click the link below. For all others you'll need to copy the link location into a subversion browser of some sort. Now you can get the cutting edge copy :-) enjoy!!
BrickLayer Subversion Repository
Tags:
Published On: 2006-01-25 14:18:04
I've been FOAF'ized Do you have a
FOAF page yet?
Tags:
Published On: 2006-01-25 13:41:38
Bricklayer RC2 Download it while it's hot. I have added a number of bugfixes and enhancements. But it's still in testing so don't plan on using this in a production environment yet :-)
Tags:
Published On: 2006-01-24 14:45:58
Click below and see a cross section of my visiting poplutation
Tags:
Published On: 2006-01-19 17:49:54
For two reasons. One it will make capturing a static backup of my site easier in the future using a tool like
wget. Two I'm using a new analytics tool and this will help me track visitor traffic in much more meaningful ways. I don't think any links you might have saved before will stop working but just in case it does... Now you know why.
Tags:
Published On: 2006-01-18 11:33:28
The Net is buzzing about BellSouth and it's rather strange view on internet access. Some details can bee seen at the article below.
Market Watch - BellSouth Story Techdirt has had some very good commentary also:
Techdirt - BellSouth It looks like someone has forgotten what exactly their product is and what the value of that product is. If you are a BellSouth customer you might do well to take a close look. An ISP sells just one thing access. And they sell it to just one market. The accessor. Google isn't using BellSouth's pipes. BellSouth's DSL customer is. The same goes for yahoo, AOL, Apple's ITunes and so on. The subscriber to the ISP requests the content. This is a PULL not a PUSH relationship. BellSouth better get it's head in the game or They are going to find they've lost their market because they forgot what product they were selling.
Tags:
Published On: 2006-01-17 14:13:24
In an earlier post about "
that guy" who can get you what you need I asked if you were the information hookup for you family, friends, and coworkers. If you are then you have probably noticed something. When the company recognizes how useful you are in that respect, you will never be left alone. It becomes a part of your unofficial job description. Saying no is just about not an option. This can get difficult. If you are lucky you will find an opportunity, in a mandate from upper managment to work on a project. You have just found a gold mine. Mine it for all it's worth. When it runs dry you'll wish you still had it. This particular mine yeilds the highly coveted "power to say no". You have an intensive job to do and it takes precedence. The information seekers will have to wait. Enjoy it while it lasts because when the project is over the avalanche will continue.
Tags:
Published On: 2006-01-15 23:25:03
But I do have a few posts brewing in my head and taking shape so watch this space.
Tags:
Published On: 2006-01-02 11:15:32
You'll have to forgive me. IIS and it's quirks just caught me at a bad time. I guess I should retract my call for murder. It's not Microsoft's fault they designed it badly. Well ok maybe it is but that's no reason to kill the programmer.... Is It??
Tags:
Published On: 2005-12-22 15:05:13
Do the world a favor, the next M$ employee you meet don't hesitate, don't think... Just strangle the life out of him. I'm so angry right now, I could kill someone. Anyone ever tried to switch SSL certificate providers on an IIS server? Your better off switching servers. To top it off, the provider we were switching to told me Microsoft won't let them link to the knowledgebase article that walks you through it. In effect, they won't allow the Certificate authority to give instructions on the proper method. So, I'm going to tell you, now that I know. You can't generate a CSR on IIS without getting rid of your old certificate for the site. This means you have either the option of being without a certificate for the 1-7 days it takes to get a new certificate or you don't get a new certificate and just renew the old one with the current provider. Not an option for my customer. Even a day with no certificate would cause significant problems. So you have to work around it. In effect, you have two choices. Actually, they are work-arounds to a "bug" that microsoft calls a "feature." You can create a dummy server to generate the request on. Then, once you have your certificate, you can export it for use on your real server. Or, option number two, create a dummy site on the real server, use it to generate your CSR, and then import the certificate into the real site. Either way, you have to do extra steps that aren't immediately obvious because Microsoft made crummy design decisions for their software. And people wonder why I prefer Open Source.
Tags:
Published On: 2005-12-10 12:12:15
I have what may quite possibly be the worlds smallest and easiest to use ajax library. It's amazing what can happen when your willing to look at things in a different way than the hype recommends. I'm gonna have a lot of fun with this. Look for it in the next preview of BrickLayer
Tags:
Published On: 2005-12-09 15:42:08
Some people just have a natural ability to distill exactly what you've been thinking and clearly communicate it. Anyone who has been wondering what this whole Web 2.0 thing is all about. Go read this article.
Web 2.0
Tags:
Published On: 2005-12-08 22:58:34
I may be modifying it a bit in the next few days probably to widen it's total size since I'd like more width for the content section but I like the overall look and colors
Tags:
Published On: 2005-12-07 23:44:39
I just finished the first draft of the Bricklayer development manual. You can see it here:
Bricklayer Manual Take a look and tell me if you see any thing that might need more clarification or spelling correction.
Tags:
Published On: 2005-11-29 12:32:22
Tags:
Published On: 2005-11-28 16:13:20
Everyone knows about AJAX these days. You just about can't go anywhere on the net whithout hearing about it. And if you're a coder who want's to know more than just what library you should download to start using it you've probably done a little googling and came up with this site:
XMLHttpRequest Objects [developer.apple.com] You even played around with the examples and made a few demo apps then realized. Hey!! How can I make these things reusable without ugly global variables and functions that check to see if the response came back yet? In short: how do I use this in a real app? Apple has done a really good job of showing how the xmlhttprequest object works. They even do a good job of showing some useful ways to use it. But if you're like me you want to go a bit farther. I like reusability. I also don't like using Global variables as a gatekeeper. So lets take a look at how we can make this code a little more reusable. The first thing to do is come up with a way to use multiple different functions as the handler for that onreadystate property. Using the same handler really cramps our style. Additionally having to write all that code to test our object's state is a real drag. It would be nice if we could avoid having to write that for every single function we use as a handler. Here is the solution: Let's start with this function here:
function loadXMLDoc(url) { req = false; // branch for native XMLHttpRequest object if(window.XMLHttpRequest) { try { req = new XMLHttpRequest(); } catch(e) { req = false; } // branch for IE/Windows ActiveX version } else if(window.ActiveXObject) { try { req = new ActiveXObject("Msxml2.XMLHTTP"); } catch(e) { try { req = new ActiveXObject("Microsoft.XMLHTTP"); } catch(e) { req = false; } } } if(req) { req.onreadystatechange = processReqChange; req.open("GET", url, true); req.send(""); } } Now for this to do what we really need it to we need a couple of different things. That processReqChange function needs to be able to change dynamically. So lets add another function argument that will hold a function passed in to be used here. Like so:
loadXMLDoc(url, func) then you can change
req.onreadystatechange = processReqChange; to
req.onreadystatechange = func; This will allow us to pass any function we want as the state change handler. Don't go deleting that processReqChange function yet though. We still need it. In fact lets take a look at that one right now shall we?
function processReqChange() { // only if req shows "loaded" if (req.readyState == 4) { // only if "OK" if (req.status == 200) { // ...processing statements go here... } else { alert("There was a problem retrieving the XML data:\n" + req.statusText); } } } We need this to keep checking our state and tell us when our response came back. We also need it to use any xmlhttprequest object we want it to. What we don't need it to do is retrieve our response for us. In short we need it to recieve a request object in it's arguments and return a response saying it's ok to process our response. So lets modify it a little shall we?
function processReqChange(req) { // only if req shows "loaded" if (req.readyState == 4) { // only if "OK" if (req.status == 200) { return 1; // it's safe now go ahead } else { alert("There was a problem retrieving the XML data:\n" + req.statusText); } } return 0; //it's not safe yet } now when we pass this function a request object it returns 1 when we have our response and 0 when the response is not ready yet. Both of these functions are now reusable. But how exactly do we start using them? I thought you would never ask. lets build an example:
function append_to_id(el, contents) { var element = document.getElementById(el) ; //alert('appending: ' + contents.nodeValue ); element.appendChild(contents); } function append(url, el) { //alert('starting append operation'); var func = function() { if (processReqChange(req)) { var ajax_return = req.responseXML; while (ajax_return.hasChildNodes()) { append_to_id(el, ajax_return.firstChild); ajax_return.removeChild(ajax_return.firstChild); } } } var req= loadXMLDoc(url, func); } In the append function we create a dynamic function that we can pass to our loadXMLDoc function. That dynamic function contains the meat of what we are wanting to do. It uses an if statement that checks our processReqChange function for a valid return. When it gets a valid return the if statement processes our request. It couldn't be any eaiser. you can see full example code here:
Example Script
Tags:
Published On: 2005-11-21 12:25:18
Tags:
Published On: 2005-11-19
My first release of BrickLayer. is ready. I'm still writing some of the documentation, but I couldn't resist giving you a peek. You can get it here:
BrickLayer And here is the documentation I have written so far:
Using BrickLayer BrickLayer Templating BrickLayer Plugin Development The BrickLayer DB Interface documentation is in progress.
Tags:
Published On: 2005-11-16 23:18:49

Details to follow.
Tags:
Published On: 2005-11-01 22:35:41
I recently wrote to Sony Music regarding their controversial DRM Technology. Below is the text of the message. Some of you may have heard of the "RootKit" controversy surrounding Sony's DRM protectes Music CD's. I recently wrote about how I trusted Open Source Technology. This is one more example of why that is. You don't always know what a Closed Source company is doing with their Software and the consequences can be disasterous. Here is the text of my message to Sony Music:
To Whom It May Concern I have recently been made aware of some disturbing facts regarding your DRM Technology for music. While I appreciate your desire to protect your investment in music labels and artists I must strongly disagree with your decision to unknowingly install "rootkit" based technology on computers that have these CD's inserted into them. I work for a company that manages networks totalling over 7000+ computers. I am now forced to advise our customers that putting sony music cd's into machines on their networks is strongly discouraged as a matter of policy. I can't take the chance that the number of security holes your DRM Software introduces will help to take down one of our networks. I regret to inform you of this but I hope that you will give it due consideration and consider altering your policy and paying closer attention to the ramifications of the DRM technology you employ. Jeremy Wall Quality Network Solutions jwall@qnsk12.com
Addendum: For those of you who don't know a rootkit is an application that embeds itself into your operating system at a very deep level and allows the creator to control your computer without your knowledge. It is often employed by a hackers to remain undetected once they are on your system. Sony's use of the technology is highly irresponsible.
Tags:
Published On: 2005-10-26 00:14:19
I'm planning to write a series of articles on Open Source Tools and Applications that I feel are ready for prime time desktop useage. I'll write one article for each app and list their relative strengths and weaknesses. If any of you read my stuff for any period of time you'll know I tend to use a lot of Open Source Software. This is for several reasons:
- I can't afford to buy commercial stuff
- Open Source is one way I am a good steward
- I trust Open Source Software
I can't afford to buy commercial I mean really. I have 5 kids, I'm technically below the poverty level, and I need a working decent computer with software to do my job. How am I supposed to shell out 300+ dollars for an operating system, 399 dollars for a low end office suite, 109 dollars for the lowest end development environment, and we haven't even touched the ancillary stuff I'd need. Furthermore I'm supposed to shell that out every couple years? I'm sorry that just won't cut it. If I had to do that I'd have to get out this business altogether.
Open Source is one way I am a good steward This relates to the above. I'm a christian so I believe God holds me accountable to how I spend my money. Hardware leaves me little choice. I don't buy top of the line but I do have to buy it. That comes out to a couple hundred here, a couple hundred there. Software is the only place I have an opportunity to cut spending. And I only have that opportunity because of Open Source. Where else can I get an entire operating system, plus a development environment, plus an office suite, plus a whole host of ancillary apps to help me do my job for the low low price of 80 dollars in a boxed set, or 0 if I have an internet broadband connection? Certainly not in Closed Source Software.
I trust Open Source Software We've all seen some of the fear being spread around about open source software. It's volunteer driven, it must be lower quality. It's a free for all, anyone might put a security hole in there. You don't have any protection if stuff goes wrong. Who do you turn to when you need help. Let me just say. I've dealt with my share of Commercial Software companies. I've had to wait on the line for tech support. I've used the commercial offerings. They all had bugs. They all people with no clue taking tech support calls. In short it's no better on the Commercial Side than it is on the Open Source. It's a level playing field. At least, if your using open source, someone like me doesn't shudder when you ask if we can help fix your computer. We might even enjoy doing it. More and more software companies are putting out software that phones home. Right now it's optional. Soon it won't be. I have no control over what information is collected about me. So what? you might say. I don't have anything to hide. Well just think about the amount of spam you get. Now multiply that by a large number and imagine your spam filters trying to cope. Outlook Express? It's toast baby. It's not just about having something to hide. Can you trust that company to keep the information safe? Maybe the company has no nefarious plans for it but what about the employees? Or that clever hacker who just found a way in. Can that company keep your information safe from prying eyes? In my line of work I read every day about some major company that had somehow leaked personal information about it's customers. Lives were destroyed, Credit Ratings went down the tubes. I'm sorry but the less people who have copies of my Info the better. I can be sure that mainstream Open Source software doesn't phone home. Because if it did, Someone out there would have blown the whistle.
I would have blown the whistle. On the whole us OSS types are pretty sharp about that stuff. So in short, I
Trust Open Source.
Tags:
Published On: 2005-10-07 00:35:57
Non-Discriminatory Databases An article about MetaData and Database Design. Mostly just a look inside my thoughts recently on Database design and on the fly expandability.
Tags:
Published On: 2005-09-28 21:17:31
FOXNews.com - Views - Massachusetts Should Close Down OpenDocument Man the litany of folks not getting it just keeps growing and growing. I'm going to say this one more time. Open Source is here to stay. Get over it and start learning how to do business in the new environment. And if someone can explain how Massachusets choosing an Open Document "
Standard" kills competition I'd like to know. I mean surely letting non microsoft providers bid on government contracts would
increase not decrease competition. And no one said microsoft couldn't offer the document format themselves. Microsoft created this environment now they can sink or swim with the rest of us in it.
Tags:
Published On: 2005-09-18 06:30:50
Stephen J Marshall
CEng MBCS CITP betrays his ignorance in an
article I found through Slashdot. Now I'm not a OSS fanatic for the most part. I use it and can't justify paying for software when I don't have to. But I don't go around yipping about how Closed Source is BAD. Folks like Mr. Stephen Marshall however really get my goat. Either they don't understand or they don't want to adapt. Let's take a look at his points one at a time.
- Intellectual Property:
Mr. Marshall brings up a favourite recent trend in OSS detractors. IP or the percieved lack of it. He brings up a point about how british patent law may conflict with most paid OSS volunteers. Namely that all the work a british programmer does is owned by his employer, Even work done after hours on his own time. All well and good sounds like the british have an IP law to fix. How does this affect the rest of the world though? Furthermore, most programmers who contribute to OSS as part of their jobs are specifically paid to do so. In those cases the company is expressly releasing their patent rights to the work by giving their programmers time to the project. In fact this is the primary form of quality OSS development. Companies like IBM, Hewlett Packard, even Dell pay programmers to work on OSS. They see it as a viable way to compete with or escape the stranglehold of a software monopoly. Welcome to the free market economy. No monopoly can exist. Someone somewhere will find a way to compete even if it means giving away their software for free. - Conceptual Integrity:
I am so tired of this argument. Mr. Marshall has obviously never tried to contribute to a thriving, quality OSS application. Believe me it's not a free for all. They don't give away CVS Commit access to just anyone. You have to prove your worth first. And there most definitely is a Gatekeeper. In the case of the Linux kernel its Linus Torvalds. And most every other major OSS endeavor out there has one too. The Gatekeeper decides what patches to take and what to leave. He decides what programmers ideas he want's to include. Yes you might fork the code to do your idea but it won't make it into the "official" version it will be a different piece of sofware. If you fork apache it's not apache anymore. And people will know it. Conceptual integrity is not missing in OSS, nor does the OSS process make it impossible to implement. - Professionalism:
Again he shows either a complete lack of knowledge concerning OSS or this is blatant misinformation. Let's look at an example. The Eclipse IDE. IBM sponsored it. A foundation monitors it. And it is quite possibly the most useful, powerful, and quality IDE out there. It also happens to be Open Source. How did an open source App get like this? Simple, IBM got it there. OSS is just another development and licensing process for a company to use. It is not a hippie free love fest with anti corporation sentiment as a required component. Somehow I think the folks that IBM pays to work on Eclipse are professional about it. - Innovation:
I don't even know where to start on this one. X-Windows is the only windowing system I know of that is network transparent from the ground up. It's been open source since the beginning. Firefox? Again, arguably one of the most innovative browsers out there. What do all these apps have in common? They have commercial backing. This guy writes as if OSS has no, and never will have any commercial backing. OSS is here to stay. The free market demanded it. Microsoft's monopoly incubated it. And now, like all segments of a free market eventually do, software development is evolving. This is not a problem, it's just the natural progression of a free market economy at work..
So what is my point? OSS is here to stay. It's time to stop worrying about it and start thinking about how you can use it, make money at it and succeed in the evolving marketplace of software development. IBM has figured it out, Novell has figured it out, and eventually Mr. Steven Marshall will figure it out or become marginalized for his failure to adapt to the new marketplace.
Tags:
Published On: 2005-09-12 21:12:24
custom dtd modular - Google Search Apparently I'm ranked just below a-list-apart on the subject of Modular XHTML and custom DTD's. How I got there I don't know. Go check out the article if you want It's kind of interesting in an esoteric way :-)
Tags:
Published On: 2005-09-12 01:44:20
Apparently some German Scholar was made aware of a very old article I wrote a while back. It is featured in his german paper at a university here. I'm not sure what he was saying about it but it is interesting nonetheless. You can learn alot by looking at what google. has to say about you.
Diplomarbeit_Michael_Homoet wonder what it says. anyone care to translate page 65 of this for me? I really have to dig that old article up somewhere.
Update: September 12, 2005 @ 09:55 The old article is located here.
http://jeremy.marzhillstudios.com/programming/archives/cat_php.html I'll see about moving to the site later.
Tags:
Published On: 2005-09-08 20:32:42
Has anyone noticed spurious referrer's showing up in your website logs? I have noticed strange referrer's showing up. It's looking like they were using bogus referrals to get their link on my log page to bump their google rating. I've now removed my logs link from the sidebar and added /logs/ to the robots.txt file so it won't benefit them anymore. But it was still annoying. Has anyone else noticed this kind of thing before?
Tags:
Published On: 2005-08-26 01:03:28
Wykiwyg With the higher profile, dynamic javascript pages are getting, look to see a lot more folks working on this stuff. And this is a wonderful example of what you can do.
Tags:
Published On: 2005-08-23 21:20:56
and of course being the bright young google lover I am I added it to my gmail client. you can now reach me using the jabber protocol, my gmail username, and google's new talk service. No sorry Gaim doesn't support the voice portion but my voice isn't that spectacular anyway. So what are you waiting for? get on the google bandwagon and contact me on google's talk service.
Tags:
Published On: 2005-08-18 04:51:55
I've noticed something today. I can make Tech Support ladies laugh. I'm quite good at it. I've had to talk to a number of tech support folks recently and they've all been women. I don't know why but it comes in handy. If you make them laugh they are much nicer when trying to help you. Now if my wife is reading this don't worry. I'm not flirting.... much.
Tags:
Published On: 2005-08-08 23:05:49
Worm.Tenga.A - nasty little virus old school style. I'm currently dealing with this baby right now in one of my schools' networks. It infects PE exe's (any windows file with an exe extension basically) and it moves
fast. It also moves across networks using public shares via the windows IPC$ browsing feature. Here are the steps for dealing with a virus breakout like this on a network.
- Shut down the network. Unplug all the machines from the network. You don't have much time cause this thing moves really quick. Especially if you have apps that run off of a network drive, Like many schools do.
- Work in segments. Take a section of the network at a time. Don't plug any machines back in until you've verified they are clean. This is very important. It takes roughly about 30 seconds for this thing to find an open share and move there.
- Educate your users. That virus may try to come back. It may have moved to a thumb drive, email attachment, burned CD, Floppy drive, or any number of other removable media sources, and you might not know. Make sure they understand the risk to the data on their network if they don't scan every file they run on the computer from one of those sources.
These things are really annoying but can be beaten with a little legwork. You'll have to get your hand's dirty with it. There is no easy fix.
Tags:
Published On: 2005-07-28 05:08:18
We are moving some servers and installing better UPS and Air Conditioning in our server room right now so it may go up and down every once and a while. Sorry for the inconvenience.
Tags:
Published On: 2005-06-26 06:07:53
I'm renovating. You may run across some different styles popping up every once and a while. I'm still figuring out how this wordpress themes thing works so there may be a some bumps in the road along the way.
Tags:
Published On: 2005-06-22 05:08:26
Are you the Information "Hook-Up" for your family and friends? Do you know the ins and outs of internet research? If someone needs something are the guy who can get it for them? Every good prison movie has the guy who can get you anything. Need a picture of Raquel Welch to hide that unsightly hole in your wall? Got a yearnin for some McDonalds food? The prison hook-up guy can get it for you. IT departments and Office area's have a similar guy. Need to find a pdf printer driver for free? I can
get ya that. Need to find information about government guidelines regarding the storage and sharing of medical data? I can
get ya that. Want to know the mating habits of wild geese? Yep, You guessed it. I can
get ya that. Just call me your Information Hook-Up. Actually those aren't even obscure items. How about this one? Need to know an easy way to get at the data in STI's School Management application? Yep I
got that too. Man... I gotta get a life.
Tags:
Published On: 2005-06-17 03:50:56
Somehow my very own tutorial on perl and CGI has made it to the top of the google search heap. I don't know how long it will stay there so I thought I'd bask while I could:
Google Search: get_cookie perl Guess I really do have to write part two of that thing now. I'm also on page two of a google search for "
perl cookies keys" I found these by perusing my logs. You can find some interesting things out by looking at those
Tags:
Published On: 2005-06-02 01:30:17
So work has bought me the perl deployment kit from ActiveState. It allows me to compile perl scripts into executables. I think it builds on the experimental perlcc and family of utilities. All I can say is wow is that cool or what. It allows me take perl scripts I design to make administration tasks easier and turn them into executables for use elsewhere without installing perl on the target computer. I can make system tray applications, system services, and standalone executables. I am going to have a lot of fun with this.
Tags:
Published On: 2005-05-25 22:58:48
My wireless company, whose name I won't reveal, has a design problem on their site. I like the company overall and have no real complaints with their service. But they did make a rather serious mistake in their online bill pay. I'm not telling them who they are since I want to give them a chance to fix it before smearing their name all over the web. Who knows maybe they will fix now that I've complained. Their customer service site doesn't really support any browser other than IE. That means firefox, Safari, Konqeror, Opera, and others can't be guaranteed to work. They know this. They didn't however elect to tell me. Or any other user for that matter. What's worse is that the site fails silently. Now this is a bad for most every application but it's especially bad for online bill pay. If you go to pay a bill online and it gives no indication that the payment didn't go through It has the potential to cause problems. Maybe even serious problems. So take note: when designing browser specific web apps make sure your users know the limitations. A notice, pop-up box, or even a cryptic error message in the page are all more desireable than silent failures. I'm in the middle of designing an ecommerce site myself right now and I intend to make sure that when it comes to peoples money they know when something didn't go right. Lessons learned from other peoples mistakes.
Tags:
Published On: 2005-05-16 01:10:02
could email you when certain events happened? It has an eventlog why can't it just email when it sees an event occur? Well I decided it was time to add just that functionality. So.. . I proudly present
EventNotifierV2.pl. A script that checks your windows eventlog for events and emails you when it sees them. Surely someone else has a need for this.
Tags:
Published On: 2005-05-11 00:26:46
One of the beautiful things about web app's is the install. There is none. Nothing. Of course web apps aren't the only thing with that feature. I hear, although have not verified, that OS X's instal process for applicationsl is as simple as copying a folder over. Many open source applications are the same. copy a folder or a binary and your done. No editing the registry. No registering services. No mess. The uninstall? Just as easy.... delete the folder. Web apps are perhaps a little superior in the uninstall department. Just don't go back to the page. No install means no uninstall either. No registry corruption. No forgotten registry entries hanging around clogging your machine. No services left behind. No mess. Computers are supposed to make your life easier not harder. And Web apps are just one more way of doing that.
Tags:
Published On: 2005-05-04 01:30:17
I am slowly getting some of my old article's back up and online. You will start to see them in the links under pages on the sidebar. As I determine they are useful or helpful I will put them back up. The first is
Perl and CGI part I I really should do part II of that one I suppose :-)
Tags:
Published On: 2005-05-03 00:06:25
The site was down last evening again. Someone unplugged the network cable. I'm not sayin who, but he was short and his name starts with a T. Anyway the server is now located in a safer environment where such things should no longer occur. (and T has received a strong lecture on unplugging strange cords)
Tags:
Published On: 2005-04-24 08:46:38
As some of you noticed the site had some downtime. This occured because I was moving the server and had some problems with DNS and then Server Hardware. (More on that in a moment) I apologize for that if it caused you any trouble. I had been noticing a growing lack of space with my other provider, so I was looking for a different solution. It presented itself through work. My Boss actually gave me a server. Quad Pentium II processors 2 gig of Ram and 30 gigs of Raid 5 disk space. He also offered to allow me to host it off his T1. All he asked was that I set up a place for the other employees to host their own websites too. This was perfect. I had a decent server with enough space to grow into and full control over the configuration. It was like having something from ServerBeach but without the cost. I look forward to using the server as a development platform to work on some experiments in Web Application development I have had in mind for some time. I hope you will enjoy my chronicling those experiences here. Unfortunately when I got the server started I was unaware that the scsi cable had a problem. It ended up trashing the raid container and I had to rebuild. This meant that after the DNS had replicated the site was down completely since the server went down. Luckily I was able to rebuild and we are now running debian sarge with everything I need to be a webhosting provider to my coworkers, immediate family, and myself.
Tags:
Published On: 2005-04-06 09:49:43
Sorry for the site downtime. I was migrating to a new server and had some problems. Anyway watch this space for an upcoming redesign and refit of the site. I'm using a new software and have a new purpose. For those of you who follow my
wife's site she suffered the same problems with the move. I think I've just about finished sorting out the mess though so it won't be long. I'll get my old archives and her site up soon.
Tags:
Published On: 2004-02-15 00:00:29
Mr. A. Russel Jones is betraying a remarkable amount of dim thinking in his article "Open Source Is Fertile Ground for Foul Play" The following is my step by step rebuttal of his arguments: Mr. Jones begins by saying: "This will happen because the open source model, which lets anyone modify source code and sell or distribute the results, virtually guarantees that someone, somewhere, will insert malicious code into the source." While I am sure many people try to do so such code doesn't get very far. Peer Review is a very powerful process. It has been repeatedly demonstrated to be superior to any commercial variant in identifying a threat. Jones goes on to give supporting statements for his premise shown above. His first supporting argument is Open source advocates rightfully maintain that the sheer number of eyes looking at the source tends to rapidly find and repair problems as well as inefficiencies—and that those same eyes would find and repair maliciously inserted code as well. Unfortunately, the model breaks down as soon as the core group involved in a project or distribution decides to corrupt the source, because they simply won't make the corrupted version public. Therefore, security problems for governments begin with knowing which distributions they can trust. Indeed? You are correct knowing which distributions to trust is a part of it. You forgot one other thing though. If the corrupted version isn't made public then it isn't really open source is it? Say said company did make a maliciouse version of say, Linux, then refused to make the source code available. Big warning flag there. The distro simply won't sell. If they release a fake version of the source code Peer Review suddenly kicks in. Someone tries them out. The notice that the kernel Binary that comes with the distro doesn't match up with a their compiled kernel. They raise the alarm. Serious Legal action takes place the again the Distro is no longer. Now take that same scenario in a Closed Source application. You don't have a way to know whether their is malicious code in the app. You have no clean apps to compare it against. You have no peer review. In short Open Source has an additional layer of protection in this scenario which Closed Source does not have. There are a number of very trustworthy distributions out there which have proved themselves. Stick with them and you will be fine.Jones goes on to say: Open source software goes through rigorous security testing, but such testing serves only to test known outside threats. The fact that security holes continue to appear should be enough to deter governments from jumping on this bandwagon, but won't be. Worse though, I don't think that security testing can be made robust enough to protect against someone injecting dangerous code into the software from the inside—and inside, for open source, means anyone who cares to join the project or create their own distribution. This one is either just outright lying, or an example of a massive lack of understanding. Such testing does not only work against known outside threats. It will also uncover malicious code in the source. Testers in open source don't just download the binary and run it. They download the source, look it over, compile it, and then run it. They may also do the same with the binary which only serves to point out differences in the binary versus their compiled version. The system of Peer review makes it practically impossible to hide parts of your source from the public without them knowing about it. Someone will notice believe me. Jones doesn't of course stop here though: Third, an individual or group of IT insiders could target a single organization by obtaining a good copy of Linux, and then customizing it for an organization, including malevolent code as they do so. That version would then become the standard version for the organization. Given the prevalence of inter-corporation and inter-governmental spying, and the relatively large numbers of people in a position to accomplish such subterfuge, this last scenario is virtually certain to occur. Worse, these probabilities aren't limited to Linux itself, the same possibilities (and probabilities) exist for every open source software package installed and used on the machines. He seems to be saying here that the possibility exists that someone in the targetted organization's IT department could use their position to use modified Open Source apps maliciously. But seriously if you hired malicious IT personnel, Open Source is the least of your problems. Open Source can hardly be held responsible for your poor hiring practices. Such a person is a danger whether your running Windows and Office, or Linux and OpenOffice. He could release a Windows virus on the network, or write malicouse VBA code for the office suite, just as easily as he could distribute modified Open Source Apps. None of Jones suppositions hold any weight and as such I fear we must file his argument under file 13.
Tags: