Note: heavy use of HTML unicode character codes here. If some of the following is garbled, I trust you'll understand.
So I have the book Programming in Haskell. So far the book seems to be fairly straightforward, except for one little hitch. Here's the start of the second paragraph of the inside front cover:
This introduction is ideal for beginners: it requires no previous programming experience and all concepts are explained from first principles with the aid of carefully chosen examples. Each chapter contains a series of exercises ...
Exercises? For beginners? Well, do beginners to programming want to read about programming or to program? Almost everyone I've ever met wants to dive in and make things which print "Hello, World" and move on from there. We want to program, so a Haskell book for beginners with exercises sounds perfect for me.
So since this is for beginners, some might be surprised to see this:
(⨁) = λx → (λy → x ⨁ y) (x⨁) = λy → x ⨁ y (⨁y) = λx → x ⨁ y
But the text builds up to this at that point and the above is understandable (the author's really good about this), but still, it's the sort of thing which makes many a student put down a book. However, that was merely a formalized definition of something and wasn't too bad. Here's what's too bad:
> [ x ↑ 2 | x ← [1..5]]
The above is a list comprehension in Haskell. It will generate a list of the square of the numbers one through five. Note that this is not the mathematical notation of a comprehension. Prior to the above horror, the author clearly gives the mathematical notation:
{ x² | x ∈ {1..5} }
So the author can't use the excuse that he was showing mathematical notation. However, the keen observer might notice a small difference between them:
> [ x ↑ 2 | x ← [1..5]]
{ x² | x ∈ {1..5} }
Hmm, what's that leading angle bracket on the Haskell version?
THIS WAS MEANT TO BE TYPED AT A HASKELL PROMPT!!!
Yes, that's right. Here's that full snippet from the book:
> [ x ↑ 2 | x ← [1..5]] [1,4,9,16,25]
The author gives code that the beginning student cannot type in! There is a "symbol table" in Appendix B which has a mapping of those funky characters to what the poor student should type in, but it's not even mentioned at the beginning. New to programming? You get to guess this. I'm not making this up. Even the book's intro to the Hugs compiler has this:
> 2 ↑ 3 8
What on earth could convince someone writing a programming book for beginning programmers that having them type in examples they can't type in would be a good idea? The rest of the book seems fine, but this is just, wow. I'm at a loss for words. And I just gave you the easy ones to figure out. You think the beginner is going to know to type \ for λ or /= for ≠?
Haven't posted in a while. In fact, much of my time has been spent with Perl 6, the upcoming version of Perl. And it's sort of been an accident.
A long time ago I stated the Perl 6 Cookbook project and recently I took another look at it. A few people have updated it, but not many. Unfortunately, I realized that the examples wouldn't run due to documentation formatting issue, so I started updating them. In trying to get them to work, I encountered the trimming blanks example.
$string = $string.trim; $string = trim($string); # or more concisely: $string .= trim;
This was a snippet of code which removes whitespace at the beginning and end of strings. I fixed the formatting issues and tried to run the code, only to discover that no one had implemented the .trim method in Perl 6. What's worse, there weren't even any "spec" tests to verify the behavior once implemented. So to make a long story short, I wrote tests for this function and submitted a patch to implement .trim in Perl 6. It's written in PIR (a sort of "object oriented" assembly language which I had to teach myself) and looks like this:
.sub 'trim' :method :multi(_)
.local string s
.local int start, end, temp, len
.local int is_whitespace
s = self
start = 0
end = length s
if end == 0 goto donetrailing
trimleading:
is_whitespace = is_cclass .CCLASS_WHITESPACE, s, start
unless is_whitespace goto doneleading
inc start
goto trimleading
doneleading:
temp = end
trimtrailing:
dec temp
is_whitespace = is_cclass .CCLASS_WHITESPACE, s, temp
unless is_whitespace goto donetrailing
end = temp
goto trimtrailing
donetrailing:
len = end - start
s = substr s, start, len
.return(s)
.end
The patch has been accepted and now I've implemented my first core Perl 6 feature. So why weren't there any tests for this? Because it turns out that .trim was never in the Perl 6 specification. Larry Wall, however, has agreed to the basic idea and it's going in the spec. After a few other issues are hammered out, I'll be extending this (probably .trim_start and .trim_end) and the spec will be updated.
So the Perl 6 spec is being changed and I've added a new core feature to Perl 6 all because I didn't double-check the spec when I should have. Go figure.
Also, it turns out that the test system for Perl 6 needs a lot of work. I started updating it to make it easier to test my changes, but that exposed other bugs in Perl 6. Looks like I'm going to be busy ...
- Mood:
accomplished - Music:Skinny Puppy | Jahya
( You can take your dogma and ... )
This is a paste from rafb.net entitled "Differences between PHP 5.2.7 and 5.2.8" and this is a problem. Andy Lester diffed the tarballs for PHP 5.2.7 and 5.2.8 and posted the result on twitter. He also pointed out the whopping huge problem we have here.
diff -urN php-5.2.7/configure php-5.2.8/configure
--- php-5.2.7/configure 2008-12-03 10:07:36.000000000 -0600
+++ php-5.2.8/configure 2008-12-07 13:31:12.000000000 -0600
@@ -2429,7 +2429,7 @@
PHP_MAJOR_VERSION=5
PHP_MINOR_VERSION=2
-PHP_RELEASE_VERSION=7
+PHP_RELEASE_VERSION=8
PHP_EXTRA_VERSION=""
PHP_VERSION="$PHP_MAJOR_VERSION.$PHP_MINOR_VERSION.$PHP_RELEASE_VERSION$PHP_EXTRA_VERSION"
PHP_VERSION_ID=`expr $PHP_MAJOR_VERSION \* 10000 + $PHP_MINOR_VERSION \* 100 + $PHP_RELEASE_VERSION`
diff -urN php-5.2.7/configure.in php-5.2.8/configure.in
--- php-5.2.7/configure.in 2008-12-03 09:54:02.000000000 -0600
+++ php-5.2.8/configure.in 2008-12-07 13:23:25.000000000 -0600
@@ -1,4 +1,4 @@
-## $Id: configure.in,v 1.579.2.52.2.116 2008/12/03 15:54:02 iliaa Exp $ -*- autoconf -*-
+## $Id: configure.in,v 1.579.2.52.2.119 2008/12/07 19:23:25 iliaa Exp $ -*- autoconf -*-
dnl ## Process this file with autoconf to produce a configure script.
divert(1)
@@ -41,7 +41,7 @@
PHP_MAJOR_VERSION=5
PHP_MINOR_VERSION=2
-PHP_RELEASE_VERSION=7
+PHP_RELEASE_VERSION=8
PHP_EXTRA_VERSION=""
PHP_VERSION="$PHP_MAJOR_VERSION.$PHP_MINOR_VERSION.$PHP_RELEASE_VERSION$PHP_EXTRA_VERSION"
PHP_VERSION_ID=`expr [$]PHP_MAJOR_VERSION \* 10000 + [$]PHP_MINOR_VERSION \* 100 + [$]PHP_RELEASE_VERSION`
diff -urN php-5.2.7/ext/filter/filter.c php-5.2.8/ext/filter/filter.c
--- php-5.2.7/ext/filter/filter.c 2008-11-02 16:04:40.000000000 -0600
+++ php-5.2.8/ext/filter/filter.c 2008-12-06 11:16:36.000000000 -0600
@@ -19,7 +19,7 @@
+----------------------------------------------------------------------+
*/
-/* $Id: filter.c,v 1.52.2.43 2008/11/02 22:04:40 lbarnaud Exp $ */
+/* $Id: filter.c,v 1.52.2.44 2008/12/06 17:16:36 scottmac Exp $ */
#ifdef HAVE_CONFIG_H
#include "config.h"
@@ -275,7 +275,7 @@
{
php_info_print_table_start();
php_info_print_table_row( 2, "Input Validation and Filtering", "enabled" );
- php_info_print_table_row( 2, "Revision", "$Revision: 1.52.2.43 $");
+ php_info_print_table_row( 2, "Revision", "$Revision: 1.52.2.44 $");
php_info_print_table_end();
DISPLAY_INI_ENTRIES();
@@ -403,7 +403,7 @@
Z_STRLEN(new_var) = val_len;
Z_TYPE(new_var) = IS_STRING;
- if (IF_G(default_filter) != FILTER_UNSAFE_RAW || IF_G(default_filter_flags) != 0) {
+ if (IF_G(default_filter) != FILTER_UNSAFE_RAW) {
zval *tmp_new_var = &new_var;
Z_STRVAL(new_var) = estrndup(*val, val_len);
INIT_PZVAL(tmp_new_var);
diff -urN php-5.2.7/main/php_version.h php-5.2.8/main/php_version.h
--- php-5.2.7/main/php_version.h 2008-12-03 09:54:03.000000000 -0600
+++ php-5.2.8/main/php_version.h 2008-12-07 13:23:26.000000000 -0600
@@ -2,7 +2,7 @@
/* edit configure.in to change version number */
#define PHP_MAJOR_VERSION 5
#define PHP_MINOR_VERSION 2
-#define PHP_RELEASE_VERSION 7
+#define PHP_RELEASE_VERSION 8
#define PHP_EXTRA_VERSION ""
-#define PHP_VERSION "5.2.7"
-#define PHP_VERSION_ID 50207
+#define PHP_VERSION "5.2.8"
+#define PHP_VERSION_ID 50208
diff -urN php-5.2.7/NEWS php-5.2.8/NEWS
--- php-5.2.7/NEWS 2008-12-03 09:54:02.000000000 -0600
+++ php-5.2.8/NEWS 2008-12-07 13:23:25.000000000 -0600
@@ -1,5 +1,8 @@
PHP NEWS
|||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
+08 Dec 2008, PHP 5.2.8
+- Reverted bug fix #42718 that broke magic_quotes_gpc (Scott)
+
04 Dec 2008, PHP 5.2.7
- Upgraded PCRE to version 7.8 (Fixes CVE-2008-2371) (Ilia)
- Updated timezone database to version 2008.9. (Derick)
As explained in a blog entry about the PHP 5.3.7 release, there was a major regression in PHP where they broke a security fix with something called "magic_quotes_gpc". Basically, it's a deprecated feature in PHP, but it helps to prevent against something called SQL injection attacks. These attacks are trivial to execute and are very serious. PHP has long been notorious for security holes, but re-opening an old security hole was so serious that PHP pulled this release and released it with the change above.
Notice anything interesting about that diff? Anything missing, perhaps?
CAN WE HAVE SOME FRIGGIN' TESTS, PLEASE? You re-open an old, serious security hole in one of the most popular programming languages, a hole you re-opened because you evidently don't have tests for it in the first place, and now you close it but don't write any tests? Have you learned nothing? Aargh!
I have some code that I've put out there without complete test coverage, but I mark this code as "experimental" or "alpha". And it's certainly not something which is a core technology that underpins much of the Web.
You ever wish you could fire open-source programmers? The next time your crappy bulletin board software breaks, remember this post.
- Mood:
disappointed - Music:Icon of Coil | TB Memory
Got an annoying little problem. I wrote the following Greasemonkey script. What this does, when I go out to the Web site use.perl.org is ignore one particularly obnoxious user by simply erasing his comments.
// ==UserScript==
// @name ignore.use.perl
// @namespace http://publius-ovidius.livejournal.com/
// @description Hide Annoying Users
// @include http://use.perl.org/*
// ==/UserScript==
(function() {
var user = 'some_user_name';
var href = '//use.perl.org/~'+user+'/';
var divs = document.evaluate(
"//div[@class='full']/div/div[@class='details']/a[@href='"+href+"']",
document,
null,
XPathResult.ORDERED_NODE_SNAPSHOT_TYPE,
null
);
for ( var i=0; i < divs.snapshotLength; i++ ) {
var node = divs.snapshotItem(i).parentNode.parentNode.parentNode;
node.innerHTML = '<p><strong>Ignoring '+user+' via GreaseMonkey</strong></p>';
}
})();
In other words, until they restructure use.perl, I will never have to see that user's comments again. However, the code is annoying the heck out of me. I had to write:
var node = divs.snapshotItem(i).parentNode.parentNode.parentNode;
That's because of how the XPath is written:
"//div[@class='full']/div/div[@class='details']/a[@href='"+href+"']",
Because I have to descend several levels deep into the node structure, I have to keep calling parentNode to walk back up the tree. With Perl regular expressions, we have 'positive look ahead assertions'. These allow me to match text which is followed by some other text, but ignore that other text for purposes of capturing data. If I had that with XPath, I could say "match any top level node followed by other nodes, but ignore those other nodes. Then I wouldn't have that awful series of parentNode calls. Is this possible in XPath?
While this poll is primarily aimed gathering information from Perl programmers, feel free to answer it if you use a different language. That being said, if you're using an XUnit framework, "number of tests" means, in this context "number of asserts" (in other words, a test method with three asserts would count as 3 tests, not one).
In the comments below, feel free to explain any answers, particularly if not all of your tests pass.
Poll #1223715 Size Does Matter
Open to: All, detailed results viewable to: All, participants: 55
Does your primary code base have a test suite?
If you answered "no" to the above question, please explain why (150 chars)
How many tests are in the largest test suite you work with?
Fewer than 50 tests![]()
![]()
8 (16.7%)
50 to 100 tests![]()
![]()
7 (14.6%)
101 to 500 tests![]()
![]()
14 (29.2%)
501 to 2,000 tests![]()
![]()
12 (25.0%)
2,001 to 10,000 tests![]()
![]()
4 (8.3%)
More than 10,000 tests![]()
![]()
3 (6.2%)
How long does the aforementioned test suite take to run?
Fewer than 30 seconds![]()
![]()
10 (20.8%)
30 seconds to 1 minute![]()
![]()
12 (25.0%)
1 minute to 5 minutes![]()
![]()
8 (16.7%)
5 minutes to 10 minutes![]()
![]()
7 (14.6%)
10 minutes to 20 minutes![]()
![]()
4 (8.3%)
20 minutes or more![]()
![]()
7 (14.6%)
What percentage of your code is covered (rounding down)?
Don't Know![]()
![]()
8 (16.7%)
Less than 50%![]()
![]()
11 (22.9%)
50% to 74%![]()
![]()
7 (14.6%)
75% to 84%![]()
![]()
13 (27.1%)
85% to 94%![]()
![]()
6 (12.5%)
95% to 99%![]()
![]()
1 (2.1%)
100%![]()
![]()
2 (4.2%)
Do all tests pass?
If you answered "no" to the above question, what percentage of your tests fail?
What is the primary language the test suite is in?
What test framework the test suite is based on? (e.g. Perl is probably Test::Harness and Java is often jUnit)
- Mood:
curious - Music:Ministry | Bad Blood
USER := $(shell whoami)
ifeq ($(USER), root)
MESSAGE = "Okay."
else
MESSAGE = "What? Make it yourself."
endif
NOECHO = @
me ::
- $(NOECHO) echo $(MESSAGE)
a ::
- $(NOECHO) echo
sandwich ::
- $(NOECHO) echo- Mood:
amused
You know, I really, really get annoyed at developers who don't even have a basic knowledge of security yet make their applications available to everyone. "Available to everyone" frequently means "web pages".
If you've ever heard of SQL injection, you know that any URL which allows its data to be injected directly into an SQL query is a security hole waiting to be exploited. So consider the basic structure of an SQL SELECT statement:
SELECT [something] FROM [table or tables] WHERE [some condition]
So any URL which has that basic structure potentially has a massive security hole allowing you to search their database and possibly cause plenty of damage. So how would you find those URLs? Enter Google Hacking. Google allows you to add a inurl: term to your query. Whatever you include with that term should be included in the URL. So what you're looking for is any URL which has select, from and where (the %3A is the encoding for a colon ':' character):
inurl:select inurl:from inurl:where
Now as it turns out, that returns a lot of questions about SQL queries in addition to URLs which execute queries. So to make it easier to find our target, let's look for anything which embeds 'cgi' in their URL:
inurl:cgi inurl:select inurl:from inurl:where
Bingo. Lots and lots of hackable Web sites. These people keep me employed.
Update: while playing around with this, I stumbled across the following URL (deliberately not made clickable):
http://140.127.211.214/cgi-bin/nlrdf_pub
Inspired by the latest horror at the Daily WTF
- Mood:
depressed - Music:Nitzer Ebb | Belief
Checked my email and saw that Josh McCadams posted his logic programming interview with me. Unless you're really interested in logic programming, I recommend you steer clear. Nothing I say will make a lick of sense.
As a side note, if you do listen and you heard me say that I only knew of one "relational database" (things like PostgreSQL, MySQL, Oracle, SQL Server, etc., are not relational),
If you're not a Perl geek into testing, this won't make sense to you. I've posted this here for them to see since I can control the formatting here.
Here's the output from the latest version of my TAPx::Parser software, a testing tool designed to replace Test::Harness and the prove utility bundled with it.
work $ runtests -lcq t/00-load..................ok t/cp_demo_lib..............ok t/cp_lib...................ok t/db_connection............ok t/dh_password..............ok t/dh_server................Failed 1/61 tests t/domain_lib...............ok t/datacentre_install.......ok t/install_sql..............ok t/general_lib..............ok t/order_lib................ok t/platform_utils...........ok t/pod-coverage.............ok t/pod......................ok t/reset_vars...............ok t/rpc_action...............ok t/rpc_dedserver............ok t/server_stock_manager.....ok t/sql_stripper.............ok t/template_tree_process....ok t/test_class_tests.........Failed 2/186 tests (less 1 skipped test: 183 okay) Test Summary Report ------------------- t/dh_server.t (Wstat: 256 Tests: 61 Failed: 1) Failed tests: 52 t/pod-coverage.t (Wstat: 0 Tests: 42 Failed: 0) Tests skipped: 3, 8, 34, 37 t/test_class_tests.t (Wstat: 512 Tests: 186 Failed: 2) Failed tests: 2, 5 Tests skipped: 3 Files=21, Tests=859, 32 wallclock secs (12.82 cusr + 2.90 csys = 15.72 CPU) |
So I started writing a post about my "fantasy" Christmas list. One idea was the idea of buying a bottle of cheap whiskey and a pack of fags for the elderly mother of my long long adopted brother and sister. However, my present list got offensive enough that it offended even me! So I'll just say that after wrapping a few presents for family members, I am having a thoroughly pleasant evening drinking rum and Coke and working on Perl 6. All I can say is that Perl 6 rocks!
# P12 (**) Decode a run-length encoded list.
#
# Given a run-length code list generated as specified in problem P11.
# Construct its uncompressed version.
sub decode(*@list) returns Array {
gather {
for @list -> $elem {
take $elem.isa(Array) ?? $elem[1] xx $elem[0] !! item $elem;
}
}
}
decode( [4, "a"], "b", [2, "c"], [2, "a"], "d", [4, "e"] ).perl.say;
And if you don't think I'm totally cool, just check out my "current music".
In other news, why the hell do I feel so guilty for not doing more to promote Perl 6?
- Mood:
drunk - Music:Massive Retaliation | Sigue Sigue Sputnik
Some of you may have heard of the infamous Black Perl poem. This poem, written for Perl 3, appeared on April Fools day, 1990 on Usenet and was purportedly from Larry Wall, though as I recall, the headers were forged. However, it's for such an older version of Perl that it hasn't been able to compile for years. So I tweaked it. It still doesn't do anything, but it compiles.
BEFOREHAND: close door, each window & exit; wait until time.
open spellbook, study, select it, confess, tell, deny;
write it, print the hex while each watches,
reverse "its length", write again;
kill spiders, pop them, chop, split, kill them.
unlink arms, shift, wait & listen (listening, wait),
sort the flock (then, warn "the goats". kill "the sheep");
kill them, dump qualms, shift moralities,
values aside, each one;
die sheep, die, reverse system
you accept (reject, respect);
next step,
kill next sacrifice, each sacrifice,
wait, redo ritual until "all the spirits are pleased";
do it ("as they say").
do it(*everyone***must***participate***in***forbidden**s*e*x*).
return last victim; package body;
exit crypt (time, times & "half a time") & close it,
select (quickly) & warn next victim;
AFTERWORDS: tell nobody.
wait, wait until time;
wait until next year, next decade;
sleep, sleep, die yourself and
rest at last
And from a friend of mine, Sparky, for whom I wound up rewriting this because of a couple of misconceptions of his (email reprinted with permission):
I was at a dive bar tonight. They had a poetry reading, open mic. and allowed non-original poetry.
I got on the net and looked up Black Perl.
I read the version at http://internet.ls-la.net/comppoems/blac
BROUGHT DOWN THE HOUSE!
Before the read I asked, "Show of hands, how many here are computer programmers?". No hands went up. Then, "How many people here have heard of the programming language 'perl'?". A couple of cheers from the crowd.
I then told the crowd, "perl stands for 'Practical Extraction and Report Language', or, as those who actually program in the language call it, 'Pathologically Eclectic Rubbish Lister'"
After explaining that the following poem was written by the creator of perl, I explained, "This poem is NOT written in English. It is written in the programming language 'perl' ", and read.
It won two awards, Best Nonoriginal Poem, and Best Poem of the night. I won Best Presentation. Kudo's to Larry, he made me look good. ;-)
After the reading I was approached by a person who said that the peom couldn't possibly compile. I invited him to download perl from ActiveState and download a copy of the poem written for the version of perl that he downloaded and test it for himself. He told me, 'That was a great poem and poems do not compile'. Then came the Zen moment, I said. 'That poem is not a poem'. From one atheist to another, I swear to GOD that is what I said!
He is probably still blinking. ;-)
- Mood:
amused
Yes folks, I take requests. Thank you very much. I'll be here all week and don't forget to tip your waitresses.
retrofire thought it would be interesting to see superimposed graphs of Bush and Blair approval ratings, so I threw one together. The data are taken from the links provided in that post.
Full disclosure on assumptions made in the graph, since "news" organizations don't typically give this: the line for Blair (the lower, green one) is much smoother than Bush's line simply because I didn't have enough data. Also, note that the approval swings appear greater than they actually are because if the graph was fully from 0% to 100%, the lines would be smoother and you'd lose detail. Also, if the poll was taken over several days, I simply took the last date. You should also check back to the original sources to understand how the approval questions were phrased. I took a bit of liberty with them to make them fit together, but I don't think the graph is too far off.

If anyone can point me to quality, longer-term data for these two, I'd be happy to see if I can put it together similar to this chart.
Update: you might notice that the graph looks different. That's because someone spotted a bug in the date handling. I've fixed the bug and updated the graph.
( And for the truly geeky, here's the hacked together program )- Mood:
geeky
I know a few Perl programmers read this blog and on the off chance that they read this and don't see it elsewhere ...
It's that time again! If you have an idea for doing some work for the Perl community and you think it's worthy of a grant, please send your grant entry to tpf-proposals@perl-foundation.org. Grant applications must be in by the last day of July and we will be awarding the grants at the beginning of September.
First, please read about how to submit a grant. Read that carefully as grants are often rejected if they don't meet the criteria. For example, if you want to submit improvements to a well-known project but there's no evidence that you have at least tried to work with the maintainers of that project, the grant will likely not be approved. You can also read through our rules of operation for a better idea of thee grant process.
To get an idea of what sorts of grants are generally accepted, you can read through past grants for 2001, 2002, 2003, 2004, 2005 and 2006. You can also read through the grant-related postings to the Perl Foundation blog. As a general rule, a properly formatted grant proposal is more likely to be approved if it meets the following criteria:
- It has widespread benefit to the Perl community or a large segment of it.
- We have reason to believe that you can accomplish your goals.
- We can afford it (we rarely approve anything more than a couple of thousand dollars).
The thorniest issue, as always, is the grant amount. If you do not include a grant amount, the grant will not be approved. So how much do you ask for? While we have information in this posting about the grant committee, the reality is fairly simple. We're a non-profit organization and we are not flush with cash. If you charge us a typical hourly rate, we probably cannot afford it. Typical grant awards are generally in the $500 to $3000 range, but we have gone under and over those amounts, depending on the grant. As a general rule the less expensive it is, the more likely it is that we can afford to fund it. For highly speculative grants (in other words, projects whose benefits may be unclear or have a high chance of failure), we are unlikely to risk large amounts of our donor's money.
Cheers,
Ovid
New address of my CGI Course.
- Mood:
geeky
Me, thinking that $40 was a bit steep for a Hasslehoff t-shirt, did some exploring and noticed they had this in their HTML:
<input name="price" type="hidden" value="40">
Well, if you know much about Web programming, you know that's an open invitation for someone to change that price to whatever they want and I wrote a couple of lines of Perl (well, 6, to be exact), which changed the price and resubmitted the form and lo!, I could buy Hasslehoff t-shirts for only $5.00. Since finding security holes is a lot more fun than trying to explain to police that you were only kidding, I didn't complete the order, I just noted that I could.
The best, however, is yet to come. Despite the Hoff's programmers having built one of the finest cars in existence, they clearly don't know squat about Web programming. Apparently, their marketing department needs a bit of work, too. Go to that site. Look at the lower left corner. If they still don't have any women's black t-shirts, the message reads "black girls out of stock".
This entry just wouldn't be complete without this.
- Mood:
amused
On the off chance that any Perl folk read this journal instead of my technical journal and are interested in Class::CGI, you can join the mailing list to help contribute to its design and to ask questions about its use.
( Details ... )Last night I received an email from one of the coordinators. He was trying to plan things, wanted photos for flyers, etc. That's when I found out I was on the list. Surprise! I don't mind since I'll be free on Saturday, but I admit that I'm caught a bit off guard.
And since I'm a geek, I must confess that I noticed something interesting about the Powell's Web site. It's probably a bad interesting and not a good interesting, but then, I don't know the technical reasons for the decision.
Let's a take a look at one of their hyperlinks:
<a href="http://www.powells.com/biblio/059
Hmm, what's that number in the URL? The link goes to this page and that number is featured prominently. It's the ISBN of the book. The use of that number immediately struck me as a potentially bad decision because you don't want to use identifying information for links like this. When new editions of books are put out, the ISBN number sometimes change (and they definitely change when a different publisher puts the book out).
I suppose they did this because you can easily link to a specific edition of a book. This is handy if there's a new edition you want to highlight. However, what happens if you have 20 ISBNs for a book, you have books in stock for 19 of those ISBNs but not for the ISBN you linked to? What if the book was published before ISBNs were introduced? (1966) Worse, what if an ISBN needs to be changed (perhaps it was entered incorrectly or the book is recalled and reissued)? Then the ISBNs on the Web site might need to be changed in multiple places rather than a single place in the database. If the hyperlink had simply used a non-identifying ID (a UUID or a database ID with an optional ISBN for a "default" edition), these problems would simply go away.
Of course, it's quite possible they've considered and eliminated all of these problems, but I strongly suspect they didn't (because I see mistakes like this all the time). If they did anticipate these problems, they probably had more work to do since they use identifying information in the first place.
- Mood:
thoughtful
Learn databases! Learn them, damn it! Learn how to design a database, understand why you should normalize your data and why you might break those rules. Understand why NULLs are a Bad Thing and why your DBA usually wants to shoot you for doing SELECT * FROM .... I am so tired of going into a database and seeing columns named "ingredient_1, ingredient_2 ...". I'm tired of fixing SQL injection attacks. Please, just learn databases, will ya!
There's a lot more about databases I could rant about, but I'll stop. Most serious developers today must work with databases of some sort or another and most suck at it. If you have a good handle on the basics, you'll trump most developers out there.
- Mood:
awake