Drupal on Lion (OS X 10.7)
I was half way done adding some info how to setup pecl/pear to my guide to running Drupal 6 on OS X 10.6 before I realized I'd been running Lion for almost nine months. So it seemed like a good excuse to update it for Lion. These might be a little wonky since I did an upgrade rather than a clean install so if you notice anything please drop me a line.
Note:I'll save you the trouble of commenting, I am familiar with MAMP but would rather punch myself myself in the face than use it. If you'd like to, go right, but I'm going to continue to compile my own so I know where everything ends up.
Working around "broken" RSS feeds
Found some more time to work on my fullfeeds project over the weekend. Finally getting into Node's everything happens in a callback spirit, and managing to not make it look like spagetti code. Discovering the async module really helped but I've probably gone a little overboard with it.
At this point it's following links, extracting and caching page content, and generating a new feed. But it's still got a way to go:
- The configuration is hard coded — I hope you like the feeds I'm interested in.
- It doesn't serve up its own feeds — on my server I symlinked its output directory into an Apache webroot to serve the files).
- Doesn't run as a service — I'm using cron to run it hourly.
Creating a sprite from cocos2d's CCTMXTiledMap
I'm working on a Pipe Mania/Dream clone for iOS using cocos2d. I was able to quickly implement the board using the CCTMXTiledMap component, but then when I started trying to figure out how to setup a sprite preview the next piece I got stuck. I'd intended to try to document all the false starts I took before figuring this solution out, but I waited too long before doing the writeup.

My map gets setup like this and I store a variable to make it easy to get to the board's layer where I'm placing the pieces:
CCTMXTiledMap *map = [CCTMXTiledMap tiledMapWithTMXFile:@"Board.tmx"];
CCTMXLayer *playLayer = [map layerNamed:@"Play"];
[self addChild:map z:-1];What I eventually figured out that I could just create my own texture from the layer's source image, then ask the layer which part of the texture the sprite should use:
CCTexture2D *tex = [[CCTextureCache sharedTextureCache] addImage:playLayer.tileset.sourceImage];
// Since I'm doing an 8-bit style I want it crisp and pixel aligned.
[tex setAliasTexParameters];
// We don't know what the piece is yet so just use the first tile.
CGRect rect = [playLayer.tileset rectForGID:playLayer.tileset.firstGid];
CCSprite *nextPiece = [CCSprite spriteWithTexture:tex rect:rect];
nextPiece.anchorPoint = CGPointZero;
nextPiece.position = CGPointMake(400, 224);
[self addChild:nextPiece];Then in my pickNextPiece method I can just adjust the rectangle:
// Pick a random tile...
nextTileGid = (arc4random_uniform(7)) + 2;
// Then ask the layer where the texture is.
CGRect rect = [playLayer.tileset rectForGID:nextTileGid];
[nextPiece setTextureRectInPixels:rect rotated:NO untrimmedSize:rect.size];Drupal for the win
My Uncle Hugh called me up last year asking if I knew anyone who could help him get a website setup—he's very polite like that—for a book he'd written on the housing crisis. Once he said he didn't really care what it looked like—and I knew I wouldn't have to do a bunch of themeing—I knew I'd be able to set him up something simple using Drupal, so of course I offered to help. It was a nice change from the normal insanity of incomplete specs and legacy requirements that the day jobs always entail. I got to just plug together off the shelf modules and enjoy using Drupal for a change.
I haven't been able to read the book yet but he's a fascinating guy so if you're interested in a first hand account of the housing meltdown go checkout The Great American Housing Fiasco by Hugh Morton. I'm going to demand a copy as payment for the website ;)
Bookmarklet to remove the NYTimes' overlay
I realized the NYTimes pay wall popups are pretty easy to bypass. Decided I'd try making it into a bookmarklet. Drag and drop this into your bookmark bar then click it after you've hit your 20 article limit:NYTimes crap-be-gone.
Drupal JavaScripting
I was trying to find some docs on how to use Drupal's JavaScript behaviors system to send to some people at work and realized that two years after D6 was released it was still poorly documented. The JavaScript and jQuery page had good examples of how to get JavaScript onto the page from a module or theme but didn't really discuss what to do from that point. I spent some time adding some documentation to the page on drupal.org but wanted to put a copy here for Google's benefit.
After announcing the change on twitter Tim Plunkett pointed out that there were already some D7 docs so incorporated those.
Using memcached with stock OS X Apache
I wanted to use memcached but didn't want to compile all the dependencies by hand and wanted to use the stock version of Apache that ships with OS X so I cobbled together the following instructions.
- Install MacPorts
- Use MacPorts to install memcached:
sudo port install memcached - Use
peclto download the latest version ofmemcachedand uncompress it:
cd /tmp
pecl download memcached
tar xvzf memcached-*.tgz - Build the extension using MacPort's version of
libmemcached(which was installed as a dependency ofmemcached):
cd memcached-*/
phpize
./configure --with-libmemcached-dir=/opt/local/
sudo make install - Enable it by editing
php.iniand adding the following line:
extension=memcached.so - Restart Apache:
sudo apachectl graceful
References:
New Mac Mini
I just picked up a new Mac mini servers to replace the one that's been my desktop machine for over 2 years. The new server version has two 500GB hard drives (as opposed to the single 120GB drive I've been working with) but no optical drive. That seemed like a bit of an inconvenience since I just bought a MacBook Air and it doesn't have an optical drive either so I picked up a SuperDrive that I can use with either.
I ran through the initial install process only to realize that I couldn't setup the drives as a RAID mirror. I found a message board thread that describes how to setup the RAID mirror involving the system disk. The short version is boot off the install DVD and use the Disk Utility to create the mirror (which will wipe the disk) then install onto that.
The install and subsequent updates is starting to approach Windows XP levels. It took three rounds of running Software Update before it finished.
The next big step was running through and updated my guide to setting up Apache, PHP and MySQL on OS X. The only change I found was that the new Java update means you need to install an additional developer package before you can really use MacPorts.
Other bits:
- Enable verbose boot mode:
sudo nvram boot-args="-v" - Show hidden files in Finder:
defaults write com.apple.finder AppleShowAllFiles TRUE
Using Mercurial and SVN
At Sony we're looking for a distributed version control system to replace Subversion—primarily Git and Mercurial. I'm very familiar with Git but hadn't done much with Mercurial so it seemed like a good idea to use it for a couple of weeks and learn the quirks. Since I'm stuck using Subversion I decided to see if it would be feasible to use Mercurial as a "super client" working locally then pushing changes back to svn. A little Googling turned up two candidates hgsvn and
hgsubversion. hgsubversion extends the commands pushing and pulling changes, giving a more native experience, so it seemed like the best choice for learning the system.
The setup is actually pretty simple but I'm documenting it for my own future reference.
Use MacPorts to Install hgsubversion:
sudo port install py26-hgsubversionEnable the rebase and hgsubversion extensions:
printf "[extensions]\nrebase=\nhgsubversion =\n" >> ~/.hgrcCheck that the extension was enabled:
hg help extensionsIt should list something like:
enabled extensions:
hgsubversion
integration with Subversion repositoriesNow you're good to checkout from SVN (note: using the svn+ prefix in the URL lets you use passwords stored in your keychain):
hg clone svn+http://example.com/svn/repo/trunkI actually ran into an issue where the clone was exploding with a stack trace. The bug had been fixed in 1.1.2 but MacPorts hadn't yet been updated so I rolled a patch to update it.
MAMP + memcache = drilling a screw in my eye
Here's yet another blog post to document something so stupid that I hope to never do it again, but know I will. I spent the better part of the afternoon trying to get the PECL memcache extension working with the PHP 5.2 part of a MAMP installation and finally managed to get it working.
Install XCode.
Open up a terminal and become the root user:
sudo suMake all the MAMP PHP binaries executable:
chmod u+x /Applications/MAMP/bin/php5.2/bin/p*Now get the memcache source, compile it and copy the library into PHP's extensions directory:
cd /tmp
wget http://pecl.php.net/get/memcache-2.2.5.tgz
tar -zxvf memcache-2.2.5.tgz
cd memcached-2.2.5
/Applications/MAMP/bin/php5.2/bin/phpize
MACOSX_DEPLOYMENT_TARGET=10.6 CFLAGS='-O3 -fno-common -arch i386 -arch x86_64' LDFLAGS='-O3 -arch i386 -arch x86_64' CXXFLAGS='-O3 -fno-common -arch i386 -arch x86_64' ./configure
make
cp modules/memcache.so /Applications/MAMP/bin/php5.2/lib/php/extensions/no-debug-non-zts-20060613/Tell PHP to load the memcache extension:
echo 'extension=memcache.so' > /Applications/MAMP/conf/php5.2/php.iniSources:
http://www.php.net/manual/en/memcache.installation.php#95063
http://blog.m-schmidt.eu/2010/03/30/develop-memcached-web-apps-with-xamp...
iOS 4 is a total waste of time on iPhone 3G
After two days of trying to use OS 4 on my iPhone 3G I'm sad to report that it's a very un-Apple like release. It's slower, it breaks a bunch of games and doesn't offer any compelling features. I'd go as far as to say they should probably have only released it for the iPhone 3GS.
Correctly accessing CCK fields in SQL queries
Update: I've written a tool to help generate this code: http://drewish.com/tools/cck-query
Twice today I've had to deal with writing a SQL query that needed data in a CCK field. The naive approach is to just look at the table and field names and plug them into your query:
<?php
$result = db_query("SELECT COUNT(*) AS count FROM {node} n
INNER JOIN {term_node} tn ON n.vid = tn.vid
INNER JOIN {content_type_date} ctd ON n.vid = ctd.vid
WHERE tn.tid = 25 AND ctd.field_date_value > NOW() AND n.changed > %d", $newtime);
?>Often this will work just fine but since CCK can dynamically alter the database schema (when you add a field to a second content type or change the number of values) the query may break.
Fortunately CCK provides functions for finding a field's table and column names so it's simple to do it correctly:
<?php
$field = content_fields('field_date');
$db_info = content_database_info($field);
?>A var_dump($db_info) gives:
array(2) {
["table"]=>
string(17) "content_type_date"
["columns"]=>
array(2) {
["value"]=>
array(6) {
["type"]=>
string(7) "varchar"
["length"]=>
int(20)
["not null"]=>
bool(false)
["sortable"]=>
bool(true)
["views"]=>
bool(true)
["column"]=>
string(16) "field_date_value"
}
["value2"]=>
array(6) {
["type"]=>
string(7) "varchar"
["length"]=>
int(20)
["not null"]=>
bool(false)
["sortable"]=>
bool(true)
["views"]=>
bool(false)
["column"]=>
string(17) "field_date_value2"
}
}
}After noting that the field has two columns and making our choice, we've got the pieces to plug into the query:
<?php
$field = content_fields('field_date');
$db_info = content_database_info($field);
$result = db_query("SELECT COUNT(*) AS count FROM {node} n
INNER JOIN {term_node} tn ON n.vid = tn.vid
INNER JOIN {". $db_info['table'] ."} ctd ON n.vid = ctd.vid
WHERE tn.tid = 25 AND ctd." . $db_info['columns']['value']['column'] . " > NOW() AND n.changed > %d", $newtime);
?>The query is a bit harder to read, but you've future proofed your code so you won't be back to fix six months from now when you reuse that date field on another node type.
Using cURL and the host header to bypass a load balancer
Users are reporting that when they load the site they're occasionally seeing stale content. We have multiple app servers behind a load balancer so my suspicion is that one server isn't expiring content correctly so depending on which one you get you'll see different data. To test this I want to write a simple script to connect to each server save the results and then compare them for differences.
The main problem to tackle is that if I try to connect to www.example.com I'll be connecting to the load balancer and then handed off to a random server. I want to connect directly to each server servers so I'll need to use their IP address. This approach would look something like:
curl --verbose 'http://10.1.1.36:8000/the_url_to_test'But since the servers use name-based virtual hosts so we'd get a 404 error back. The trick is to have curl send the proper host header:
curl --verbose --header 'Host: www.example.com' 'http://10.1.1.36:8000/the_url_to_test'This gives us the output we need to start writing the comparison script.
Using logrotate and drush for daily Drupal backups
If you've got Drush installed—and you really should—you can use the following recipe to setup a backup system that will maintain daily backups for the last two weeks. Most of the logrotate configuration is based on a Wikibooks book that I found.
Flash CS4 Gotchas
I've been banging my head against Flash for the last few days and started trying to document a few things.
Can't import fl.controls
For some reason Adobe didn't include them by default so you'll need to add the path to the project.
- Open the File > Publish Settings... menu item
- Click the Flash tab
- Click the Settings... button
- Click the Source Path tab
- Click the + button and paste in:
$(AppConfig)/Component Source/ActionScript 3.0/User Interface
Can't use a Tween on a scrollRect
The Tween class can only change a simple property and the scrollRect need to be changed and the reassigned before it will update. The solution is to add new property to the class and Tween that instead:
public function get scrollX():Number {
if (this.scrollRect) {
return this.scrollRect.x;
}
return 0;
}
public function set scrollX(value:Number) {
var r:Rectangle = this.scrollRect;
if (r) {
r.x = value;
this.scrollRect = r;
}
}Then you can use a Tween:
tween = new Tween(this, "scrollX", Strong.easeOut, scrollX, scrollX + 100, 1, true);Also, you'll want to keep a reference to the Tween object so that it doesn't get garbage collected half way through the animation.
Can't use named HTML entities
Flash's TextField only supports a small subset of named HTML entities (< > & " '). If you're displaying HTML from users or a CMS you'll find that things like &deg; slips by so you'll need to convert the named entities to their numeric versions.