
To accomplish this, I locked myself in my little computer room for longer than I care to admit. This is not uncommon. These website projects tend to overwhelm me. I lose all sense of time until I finish.
Here's where I knew I approached bottom: as part of the new design, I'm resizing all of the thumbnails and photographs to enlarge them for today's bigger monitors. Because of server limitations, I have to manually generate each photo album (as opposed to creating a batch project that does all of them overnight). It takes anywhere from two to ten minutes to create the album on the server. Even though it was not important that I finish today, I found myself unable to stop. I would tell myself, last one. I can continue tomorrow. But instead of getting up, I would decide to wait a few minutes longer until it finished. Then I would click generate on the next one, and tell myself just one more. You have no idea how addictive generating photo albums is for me.
It's similar to the MMORPG video games I play. While large parts of those games are not fun, I continue to chase the carrot at the end of the stick. No matter how often the carrot moves, I just tell myself just one more.
My coding continues to go smoothly. I hit a snag yesterday as I worked on an administration function. I attempted to display all the photos that did not belong to a post, the orphaned photos, if you will.
In the old sewcrates, I tied the photos to individual posts. The photos were uploaded to the post’s directory (remember, each post had its own directory that contained flat files and attachments), and I generated the /slides and /thumbs as subdirectories for the posts.
In NAIS, I decided to separate the photos from the posts. Each photo (and I use photo loosely—it includes music and videos as well) lives in a table and in the /photos directory. I generate the /slides and /thumbs as subdirectories from /photos, and use a table in the database to tie the posts and photos together.
I designed it this way to allow for different types of albums. Besides the post-based albums (what you currently see on sewcrates), I wanted to create albums based off tags. For example, I could view an album with every photo tagged with "Julie." While the functionality will be there, I still need to go back and tag all my old photos. This is not something I'm rushing to do. I’m planning to build in functionality to allow for batch drag and drop tagging of photos. It may not make it until 3.1 as I’m getting anxious to post NAIS.
Let's get back to my snag with the orphaned photos view. At first, this seemed easy. I used my photos index code to generate a page with a group of photos. I just needed the MySQL query to finish it. I thought I had gotten rather good at writing the queries. When I coded castofhorribles.com, I spent a lot of my time looking up the format of the queries. After coding NAIS, I no longer have to refer to the documentation to generate most queries.
The problem seemed simple: find me all entries from the table of Photos where there is not a corresponding entry in the table of PhotosPost (the table that ties together Photos and Post). Here’s the abbreviated Data Dictionary:
Post
PostKey int(11)
Title varchar(1000)
...
Photos
PhotosKey - int(11)
Filename - varchar(100)
…PhotosPost
PostKey int(11)
PhotosKey int(11)
Order smallint(11)
This stumped me for a while. I can easily write a query to find all photos in a post, or all posts that a photo belongs to, but when it comes to finding something in the negative, I hit a huge wall. That’s where the magic of sub-queries came in. After much head pounding and Googling, I arrived at the following query:
SELECT DISTINCT * FROM Photos WHERE NOT EXISTS (SELECT * FROM PhotosPost WHERE Photos.PhotoKey=PhotosPost.PhotosKey)It looks complicated, but it’s actually very easy and logical. You don’t even need the Post table. What you’re saying is find me all photos where the photo does not exist in a post. The key insight I had was that you can refer to columns from the first query in the sub-query. In the above code, the Photos.PhotoKey is referring to the Photos column in the first query. Once I realized this, everything else fell into place.
This page from the MySQL documentation provided me the insight. Notice the second example:
SELECT DISTINCT store_type FROM stores WHERE NOT EXISTS (SELECT * FROM cities_stores WHERE cities_stores.store_type = stores.store_type)This query finds what kind of store is not in a city (which is very similar to asking what photo is not in a post).
This is a long way of saying that I regret not taking a database class during any of my computer science degrees. I can’t believe it’s not a required class. I never understood the value of databases until I started working with them. To give you an idea, let’s look at the lines of code in NAIS compared to the old sewcrates: the original sewcrates had 2,287 lines of code.[1] The new sewcrates has 1,034 lines of code.[1] While I can attribute some of the improvement to more efficient design, the majority of the weight reduction is in the removal of flat file parsing. I replaced what used to take hundreds of lines of code with a simple MySQL statement. Isn’t modern technology wonderful?
[1] The lines of code are approximate. I used: grep -c ";" *.php on my two code directories. It’s not perfect, but it provides a decent sketch.
After posting my Painfully Regular Expressions, Chuck commented that on his site, he threw the entire URL to the handler and let PHP parse it.
His RewriteCond probably looked something like this:
RewriteRule ^(.*)/?$ /handler.php?url=$1And his PHP code probably looked like this:
$url=explode('/',$_GET['url']);Alternatively (and where I ended up where I ran into problems with the way rewrite handled ampersands (&) in the tagnames, e.g., http://sewcrates.com/tags/D&D):
RewriteRule ^(.*)/?$ /handler.phpAnd the PHP code:
$url=explode('/',$_SERVER['REQUEST_URI']);Both of these, by the way, beats the crap out of my PHP code for my original RewriteCond formulation:
$url=array($_GET['a'], $_GET['b'], $_GET['c'], $_GET['d'], $_GET['e']);And has the added benefit of handling any number of parameters.
Sigh. This is one of those situations where my own cleverness got the best of me. I’ve since reconverted my .htaccess file to Chuck’s more elegant solution. I leave it to PHP to parse the URL. And with this egg on my face, I’ll return to my little programming hole.
One good thing did come of my earlier exercise. I learned the basics of regular expressions. After reading Chuck's mail, I created the new RewriteCond in only a few seconds. Before I went through my Painfully Regular Expressions, it would have taken much longer. I could have probably found Chuck's solution by Googling a bit--but I wouldn't have learned much. And look how many words I pushed to explain my failure!
My NAIS development work has been going relatively smoothly. I managed to converge on a decent database structure, and convert all my text posts and cast of horribles doodles into this structure. I even managed to write code that displays individual posts (text, photos, and cast of horribles), the home page, and posts based on tags. I began fiddling around with a new format for the posts, trying to create a cleaner look. I settled on the Calibri font from Windows Vista, with a fallback to Verdana and Arial. The posts themselves are simpler, stealing a few ideas from Kottke .
I originally used PHP 5’s new object-oriented (OO) structure, which provides much more OO power than PHP 4. That’s where I ran into problems. Most of the original sewcrates.com code was written using functional code. I used OO for some of the more complicated structures—such as the parser that indexed all the directories to generate my musing collection, and the code around the generation of photos. I saw NAIS as an opportunity to rewrite everything using an OO model.
It’s been a while since I effectively used OO design. (A “while” is a bit of an understatement. The last time I effectively used OO design was in graduate school before the millennium.) For the most part, I use objects as advanced structures to provide common global variables for functions to share. For NAIS I wanted to develop a more meaningful OO design, and I dived in before fully planning the approach. I ended up with a strange design that left me struggling.
As I looked through my code files, I noticed that I created too many objects, overusing inheritance (not multiple inheritance, which is not available, thankfully). I’ve since gone back and removed most of the OO use. OO is very useful in some areas, but it tends to be overused, especially by me. In replacing the classes with functions, I did find a bunch of places where I could have used the class to limit how many variables I pass in related functions. There is an advantage to passing variables, however. I can control (and more importantly understand) what parameters the functions are using.
Updated: Chuck provided me with a much more elegant solution than described below. It's explained fully in Over-Thought Solutions.
I originally used the Apache rewrite engine to parse and rewrite the URLs to call my handler.php file with appropriate variables, e.g., translating:
- http://sewcrates.com/tags/Programming/
to
- http://sewcrates.com/handler.php?type=tags&tag=programming
While it worked reasonably well, I decided to scrap it and move the parsing code to the PHP handler to make it easier to manage and change.
It ended up being more difficult to code a generic rewrite condition than the individual rewrites that converted the URLs to their specific types. It was the regular expression used by Apache that required much work. I’ve used regular expressions rarely, and when I do, I try to find an example of what I want to do and copy it. This time, I decided to go to the source and actually learn what I was doing (mostly because I couldn’t find a good example). My sources:
- Wikipedia - the Syntax section labeled “Traditional Unix Regular Expression.”
- Apache RewriteRule Directive documentation the “Some hints on the syntax of Regular Expressions” section.
My goal was to capture all URLs with 0-5 parameters, e.g., rewriting
- http//sewcrates.com/
- http://sewcrates.com/first/
. . . - http://sewcrates.com/first/second/third/fourth/fifth
to
- http://sewcrates.com/handler.php
- http://sewcrates.com/handler.php?a=first
. . . - http://sewcrates.com/handler.php?a=first&b=second&c=third&d=fourth&e=fifth
I used a-e because 1-5 didn't work in the Apache rewrite statement. In the handler.php code, I would take $_GET['a'] through $_GET['e'] and use them to identify the type and content of the URL. Here’s what I (finally) came up with for my .htaccess file:
DirectoryIndex handler.php
Options -Indexes
ErrorDocument 404 /handler.php?a=error&b=404
ErrorDocument 403 /handler.php?a=error&b=403
RewriteEngine on
RewriteCond %{REQUEST_URI} !/handler.php [NC]
RewriteRule ^([^/]*)/?([^/]*)/?([^/]*)/?([^/]*)/?([^/]*)/?$ /handler.php?a=$1&b=$2&c=$3&d=$4&e=$5
I’m not sure if it’s the best way, but after much testing, it does work. Besides my normal technique of “adding one when in doubt”—or in this case adding a question mark, there is some logic behind the expression. I’ll try to break it down. The important piece of code is the RewriteRule line, which is made up of two parts: the match and the result.
Match: ^([^/]*)/?([^/]*)/?([^/]*)/?([^/]*)/?([^/]*)/?$
Result: /handler.php?a=$1&b=$2&c=$3&d=$4&e=$5
If the URL fits the Match, then Apache rewrites it with the Result. The Result includes a bunch of variables. For each (…) statement, Apache rewrites it to a URL variable (the $1-$5 represents the results of the regular expression). I found regular expressions that captured an absolute amount of parameters, but not one that loaded arbitrary variables.
The Match was the difficult part. To start with, regular expressions are anchored by two characters: it starts with ^ and ends with $. Removing those from the expression leaves us the meat:
([^/]*)/?([^/]*)/?([^/]*)/?([^/]*)/?([^/]*)/?Each (…) statement corresponds to a variable in the Result code, ranging from $1 through $n, where n is the number of (…) statements. All of the (…) statements look like this:
([^/]*)If you look at the references above, you’ll see that the [^…] code means match any character except the one that follows the ^. In this case, I excluded the forward slash, which is the end character. The * after the [^/] tells the expression to repeat this match from 0 to n characters. In other words, match all characters until you find a forward slash.
Between each (…) statement, I included a /?. The slash is the character I expect to find between each expression (e.g., http://sewcrates.com/first/second/). The question mark means match 0 or 1 of the slashes. The advantage of using the question mark is that it allows for there to be zero slashes, or, in other words, it allows for less than five parameters.
That’s it. I use the RewriteCond to exclude the files and directories I want the server to find, and the rest get reformatted into this code. My handler.php file captures the results and parses it using the $_GET[‘a’]…$_GET[‘e’] variables. From there, I figure out what page should be sent to the client.
After learning regular expressions, I dived into the Perl documentation, and was amazed at how good Perl was compare to PHP (or even Python, which I fell in love with earlier in the week). It’s amazing how powerful and easy to use Perl is. I see where PHP steals many of its ideas (and then goes about badly implementing them). I spent many hours poring over the documentation, thinking I was going to change NAIS to use Perl. Like my earlier decision with Python, I decided to stick with PHP. It seems I fall out of love as fast as I fall in love with languages.
The coding process for the secret NAIS project is in full swing. I managed to port over all of my text-based postings to my new database. I am creating NAIS in three parts: retrieval (URL parsing and database fetch), display, and editing. I’m not yet decided on how or where editing will live. In the current sewcrates, all three mostly live in the same place (albeit separate source files). I’m trying to better abstract this relationship.
After reading today’s Xkcd comic, I spent about an hour reading through the Python documentation and growing very excited by the syntax of the language. It seemed to do things correctly compared to PHP, which is a badly designed language. Easy example: every time I call a string command, I have to look up the ordering of the parameters:
mixed str_replace ( mixed $search , mixed $replace , mixed **$subject** [, int &$count ] )
int stripos ( string **$haystack** , string $needle [, int $offset ] )
Note that the haystack variable (renamed $subject in the case of str_replace) is in different places for these two related functions. It is infuriating.
Even with all the problems I have with PHP, I decided to stay with it for NAIS. There seems to be some issues with the speed of Python-related webpages in Dreamhost. Additionally, it’s not a good idea to start a large project to learn a new language. It makes more sense to write smaller pieces of code. I don’t want to finish NAIS, only to have acquired enough elegance with Python to want to rewrite it.
Update: I'm not sure if what I write below is true. There are obviously issues with sewcrates and search robots, but I'm not certain I found the culprit. (The fact that I'm stupid is not at issue.)
Okay, so I’m an idiot. This is what happens when you write your own blogging software. You miss things. I was interested to see what would happen when I started posting programming/technical advice. A few days later, I received a referral from Google for my Internet Sharing on the Q9 post. Excellent, I think. My name is out there. I’m in Google. I received a visitor.
I then checked the referral results, and noticed that Google does not link to the article, but to the front page. That can’t be right. If it links to the front page, then it will be gone in a few days, and when people visit, they won’t see the post unless they checked the Google cache.
It took me a few short searches on “Permalink” to realize my mistake: I never added the rel="bookmark" to my permalink (best source and Wikipedia: Permalink). Here I was, running around without real permalinks. I even titled the links “link back,” thinking those magical words would mean something to the search robots. (They don’t, of course.)
A few coding changes and I added the rel="bookmark" code to all of my permalinks on sewcrates and castofhorribles. If I wasn’t so stubborn, I would fall into the welcoming embrace of MoveableType or Wordpress. Thankfully, I’m terribly inflexible when it comes to programming.
I finally got my Flickr account working with sewcrates.com. I created a couple of sample photosets, and went about displaying them on a test page. It worked, but I’m disappointed. The Flickr terms of use limit the photos displayed on a page to 30, and a link back to the Flickr page from each photograph. There is also a rather significant delay as the API calls work through the Yahoo! systems. The photo download speeds once you have the list were quite good. It would be trivial to cache the results on a static page to avoid the delay. This doesn’t get around the terms of use limitations, however.
While coding, I ran into one snag: the Flickr API documentation uses PHP’s file_get_contents() to read the Flickr serialized output. Dreamhost does not permit the file_get_contents() call for URLs (the flag is allow_url_fopen in the PHP.ini file). Dreamhost’s wiki provides a very easy workaround using cURL.
The PHP serialized Flickr call function is as follows:
// On success: returns array with Flickr data related to $params; Echo calls+error and returns FALSE on failure
function flickr($params,$use_userid=false) {
if ($use_userid)
$params['user_id']='*'; // your user_id goes here (for convenience)
$params['api_key']='*'; // your Flickr api key from http://flickr.com/services/api/keys/ goes here
$params['format']='php_serial';
$encoded_params = array();
foreach ($params as $k => $v)
$encoded_params[] = urlencode($k).'='.urlencode($v);
$url="http://api.flickr.com/services/rest/?".implode('&', $encoded_params);
$ch = curl_init($url);
curl_setopt($ch,CURLOPT_RETURNTRANSFER,1);
curl_setopt($ch,CURLOPT_CONNECTTIMEOUT,5);
$rsp=curl_exec($ch);
curl_close($ch);
$rsp_obj=unserialize($rsp);
if ($rsp_obj['stat']!='ok') {
echo 'Flickr call failed:<br>';
print_r($params);
echo '<br>';
print_r($rsp_obj);
return false;
}
else
return $rsp_obj;
}
?>
I created the following code to test the Flickr calls:
function flickr_sets() {
$params=array('method'=>'flickr.photosets.getList');
$sets=flickr($params,true);
$count=0;
$sets=$sets['photosets']['photoset'];
foreach($sets as $set) {
$id=$set['id'];
$title=implode(' ',$set['title']);
$description=implode(' ',$set['description']);
$count=$set['photos'];
$params=array('method'=>'flickr.photosets.getPhotos','extras'=>'date_taken','photoset_id'=>$id);
$photos=flickr($params,false);
$photos=$photos['photoset']['photo'];
foreach ($photos as $photo) {
$params=array('method'=>'flickr.photos.getSizes','photo_id'=>$photo['id']);
$sizes=flickr($params,false);
echo '<img src="'.$sizes['sizes']['size'][0]['source'].'" alt="">';
}
}
}
?>
While it was a fun exercise, I decided not to use Flickr to host my photos. There are too many limitations on how I can use the APIs. That means more coding for me, as I figure out a good way to store the photographs in my database. It’s also provides more flexibility in tagging and displaying the content. This means back to the drawing board on my database design.
After much thinking, I’ve decided to move away from my proprietary photo galleries, and into the welcoming (and API-ful) hands of Yahoo!’s Flickr. I spent many hours over the last two days trying to get my too smart phone to upload my photographs automatically to Flickr. So far I failed, but I am enthusiastic that if I spend another ten or so hours on it, it will eventually work, and everyone will revel in blurry photographs of my dog and my coffee cup. At least for the three days I actually take photographs with my too smart phone, before growing bored with the effort and realizing that I go to the same places and do the same things every day, and such things are, to put it mildly, not even mildly interesting.
I installed ShoZu on my phone. ShoZu tries to add value by providing its own web service where you send the photos before they route them to Flickr. While I understand ShoZu’s attempt to add value (and get eyeballs), it fails in annoying way. ShoZu required me to set up an account that I will never visit. It also didn’t work (although they may have something to do with my upgrade to Windows Mobile 6.0.) What I want is a simple application that attaches itself to my phone’s camera and sends the photo to Flickr directly after each photograph is taken. Somewhere between my phone, ShoZu, and Flickr, my photographs are getting lost. The ShoZu application tells me that the photos were properly uploaded to ShoZu. But the ShoZu website doesn’t find them. This is what happens when you add a middleman where it’s not needed.
Clearly, getting my phone to upload to Flickr is not an important part of my redesign of the photo section of sewcrates. What I need to do is use Flickr’s APIs to display sets (or is it groups?) of photos in NAIS. This also simplifies my website design, as there will be only four types of posts: text posts, photo albums (pulled from Flickr), Horribles, and lists (perhaps tied to Amazon’s service as in the current sewcrates). I would love to replace my proprietary movies and books list with a third-party service, but I haven’t found one with a decent API.
That’s a long way of saying I’m making progress, but it’s slow. I haven’t written a line of code yet.
After failing at my database design, I decided to take a step back and work on easier issues first. How should the new URLs look? The following are my thoughts mostly in the order they came. This is not a presentation of results, but a look at how my brain weaves its way through different mazes in an (in this case unsuccessful) attempt to arrive at a solution.
I started with this given: all old URLs will continue to work, at least for the older posts so as not to break anything. I decided to move away from the current URL design because for the most part it doesn’t provide useful information.
My first attempt:
http://sewcrates.com/Archive/2007/Database_Shmatabase/
http://sewcrates.com/Archive/2007/Database_Shmatabase_2/ (for duplicate titled entries)
http://sewcrates.com/Archive/2007/Database_Shmatabase_2/photo_001.jpg (the URL for a photograph uploaded and referenced in the above post)
To complicate matters, here’s the URL for an individual post for the img_013647.jpg file. This allows photos to exist outside of their photo albums or posts, so that tagging can be used to sort and display them. I’ll use the same process for doodles, and movie and book lists and reviews. I will also need to automate the creation of the albums. Many times I do not have the patience to do much more than upload lots of photos and assign a name and global tag. I want the ability to go back (or have Julie go back) and individually name and tag the photos at a later date. (This is how the current version of sewcrates works, although most of the photo information is contained in the album and not tied to the individual photograph.):
http://sewcrates.com/Crazy%20Monsters/2007/img_013647_jpg/
I’m still not decided on the year part of the URL. In the current sewcrates, I used the full date format, along with the category (what will now be tags), e.g., http://sewcrates.com/Archive/2007-11-26-15:21:36/. I realized that most people do not care much about the full date. I used the full date, including the time (and microsecond, because, you know, sometimes I posted twice in the same tenth of a second), to allow multiple posts within a single day. The original version of sewcrates.com supported only one post per date: http://sewcrates.com/2007-11-26/ (at least that’s how I remember it).
It is a bit strange to see the year in the URL in this way. For example, http://sewcrates.com/Crazy%20Monsters/2007/img_013647_jpg/ may be followed by http://sewcrates.com/Crazy%20Monsters/2008/img_019234_jpg/, which is a bit confusing from a logical perspective. With that said, when I am reading the internet, I do enjoy glancing up at the URL to see what year the post was made. This quickly clears up whether this is something brand new, or a very old archive. The month and the day, while important, do not fulfill this purpose, and make the URL unnecessarily complicated (at least more so than it is currently). Alternatively, I could just use http://sewcrates.com/Crazy%20Monsters/img_013647_jpg/. This doesn’t help with identifying the year of the post—but the post itself will provide that information. So many decisions to make!
The other advantage to the single year is the directory structure. While I attempted to use the database to store my doodles in castofhorribles, I realized it was not efficient, from both a storage and speed perspective. (I now save all my doodles in both file form and in the database. While I’m able to read both the database and file versions of the .png files, I cannot read the .ai from the large blobs in the database. This may be a MySQL limitation on large files (my .ai files are usually >1.5mb). I haven’t done the leg work to check on this, since I don’t use any of these tables to display the files. This may change in this or the next version. But that’s a much longer discussion.) I want to store all files in a directory. So, continuing with the above example, I will store the jpg in: http://sewcrates.com/2007/img_013647.jpg. The advantage is I am creating fewer directories. The disadvantage is that there will be a larger number of files in each directory. I don’t see any advantage either way. I’ve almost argued myself into returning the month and date to the URL. It’s an easy enough switch once I start programming, so I’ll leave this on my list. For the record, my doodles are stored in http://castofhorribles.com/doodles/. It’s one large directory with all the files. Since I guarantee a unique filename for each doodle (I base it on a simplified version of the title, and add “_n” if it’s a duplicate), there should never be a conflict in the directory.
As I think more about this (man, this is becoming much more of a planning exercise than I originally realized), I’m thinking of moving the category away from the first part of the URL. I included it in the URL for the current version of sewcrates because it was the easiest way to provide for stepping through the different posts. For example, you could use the next/previous links to move through all of my http://sewcrates.com/Writing/ posts, such as http://sewcrates.com/Writing/2007-08-21-00:00:00/ to http://sewcrates.com/Writing/2007-10-28-23:14:16/. In the next version of sewcrates, there are larger issues I need to contend with. What if a user wanted to see all photos with Julie in an album format (similar to how I use tags to show multiple Horribles: http://castofhorribles.com/tags/Julie/) There are two ways of providing that: the first is to open it in a large album, something like: http://sewcrates.com/Photos/Tags/Julie.
I also have the option of using variables in the URL: http://sewcrates.com/2007/?tags=albums+julie;years=2007+2006. (You can do this two ways: present this as a URL, or convert this complicated URL into a slash-based one in the .htaccess file. That’s how castofhorribles works.
I’ve written so much and decided to come back around:
http://sewcrates.com/img_013647_jpg/tag1/tag2/tag3/etc - where tags can be search terms: “years=1973-1976” this will show one image, and the next/previous buttons will allow you to step through other posts that meet the tag criteria in chronological order.
http://sewcrates.com/album/tag1/tag2/tag3/etc - this is more complicated. Here is how it will work: For albums that contain only one type (and the type will be included as a tag, e.g., Photos, Books, Cast of Horribles, Movies, Musings), then I will present a thumbnail version of the page. Thumbnails for photographs and doodles are the smaller version of the picture; for text posts, it’s just the title.
http://sewcrates.com/list/tag1/tag2/tag3/etc - this is the thumbnail for multiple types of posts, e.g., provide on one page, all the thumbnails with Julie, which would include thumbnails (if available) for photographs, doodles, and posts. (How it will be formatted will be a huge challenge. I think I did a good job with the photo thumbnails in sewcrates and the indexed thumbnails in castofhorribles because they were all of the same size. If we throw in text titles and different sized thumbnails, I’ll run into huge problems. But, again, that’s for another time.)
http://sewcrates.com/about/ - the URL for these “special” pages will remain the same.
I will store all files in a single /data/ directory, controlled by a simple name with the same rules as for castofhorribles.
That means I’m dropping the year from the URL. This provides more flexibility with only a small loss of readability. The years will still be available in the posts themselves. I’ve said a lot (most of which I later contradicted), and programmed very little.
Everything is a tag. The tags may have names: location=Seattle, WA. That’s a tag. This enables you to create a post with a Seattle, WA title and not confuse the system. For titles, you have title=This is a nice title. And then you create the simplename from that title that is used in the URL.
This allows me to create albums by referring back to titles. This is complicated, though. Wouldn’t a date be a tag? And in this case, I might be overloading what I mean by tags. A tag should not be so complicated. It’s just a list of items. I could yank out all the specific tags. I’ll use it for two purposes: to categorize and to provide information. That way everything is contained within a single table. But to what end? It allows me to change and add templates without worrying about changing the database. Is that helpful? I’m not sure.
As I continue to think about this, other issues pop out. Do I want to have the flexibility to have parent/child relationships between photographs (or assets, as Moveabletype calls them) and larger posts? Or do I want to tie them together through tags of tags, i.e., tags where there is a “type” and a “value” field, such as “title=This is a long stupid title.”
Ugh, my brain hurts.
For those who care nothing about programming, you might want to skip these entries. I plan to keep a journal of my programming efforts to rewrite sewcrates.com. I do this for two reasons: first, whenever I program, I tend to come across a technical problem that I cannot solve with a simple Internet search. I figure there are other people on the interwebs who come across similar problems. My hope is for these entries to alleviate some of the pains of finding these solutions. Second, these entries provide me both an excuse and a topic for writing. While I rarely need an excuse to sit down and throw down words, I often flounder (a word I use more and more as I replace consternations with unorganized babbling) without a topic.
I’m developing NAIS (the New And Improved Sewcrates.com) using PHP and MySQL, both running on Dreamhost’s servers. I’m not sure how much code I will use from the original sewcrates.com codebase. I do hope to reuse the code I wrote for the Cast of Horribles rewrite.
Ideally, I want to use Microsoft servers and .NET through C#, not only out of corporate loyalty, but because there are excellent (and free!) development tools. The problem is the Windows Server hosting plans are awful. There is a huge opportunity for a Dreamhost-type hosting company to create a similar business model for Microsoft-hosted solutions. Here are some comparisons of Dreamhost against a few of the top search results for “windows hosting” (most of these hosts provide overpriced Linux hosting as well):
| Host | Disk Storage | Monthly Bandwidth | Monthly Price | OS and Database |
|---|---|---|---|---|
| Dreamhost | 500 GB | 5 TB | $5.95 | Linux and MySQL |
| AccuWebHosting | 1 GB | 10 GB | $15.00 | Windows Server w/ASP.NET and Microsoft SQL 2005 |
| JodoHost | 2.5 GB | 35 GB | $18.95 | Windows Server 2003 and Microsoft SQL Server |
| iPowerWeb | 100 GB | 1 TB | $9.95 | Windows Server 2003 and MySQL |
| Verio | 20 GB | 500 GB | $19.95 | Windows Server 2003 (includes SSL and dedicated IP) |
This is a very high level comparison that does not take into account all the different features or reliability of the different hosting companies. (I did a double take at iPowerWeb. If it provided Microsoft Exchange mail services, I would probably switch.) What I was trying to show is that the price per month of Windows-based services that support ASP.NET and Microsoft SQL Server are for the most part, not comparable.
Dreamhost is an exception even for Linux-hosting companies: they have a very strong business and understand the market forces. They freely admit that the oversell, i.e., they sell more bandwidth and disk space than they can supply because they know people like me will only use a fraction of the available space. I appreciate that. I also appreciate that it is there when my site one day gets more than five hits.
One of my biggest pet peeves with these hosting companies is available disk space. Disk storage is relatively inexpensive, even taking into account necessary backup storages. Services that limit me to 1-20 GB are completely unacceptable.
Before the Marathon this year, I started a database design to hold NAIS. I plan to move the sewcrates’ data store from flat files to a database. The big draw of the database was the ability to create extensible pages through templates. I wanted to create pages that showed individual and groups (in both full and thumbnail fashion) of: text posts, doodles, photographs, book reviews, reading lists, movie reviews, etc.
I hit my first roadblock after thinking through the database design to enable these templates. I sat down with my data dictionary from the database, and realized this was a much bigger project than I expected. It always works like this: I get these huge ideas, and then I get stuck. Once I work through the ideas, and start programming, I usually remove the stickiness (there are times, of course, where I end up giving up halfway through). I keep looking to WordPress and MoveableType and asking myself why I feel a need to reinvent a perfectly round wheel. I then remember that it’s because I can, and it keeps me occupied and happy and provides a strange contentment that is difficult to explain.