Graham Christensen

Symfony development, hardware, and everything else too.

Using `make' to Manage Jekyll

Recently I switched from using Wordpress to Jekyll. I noticed a general trend of using rake for managing the generation and deployment of the website. Lately I’ve become quite fond of straight Makefiles, and ported their concepts and utilities.

The resulting Makefile is fairly basic, but performs essential operations:

  • clean: Delete the locally built files
  • build: Generate the website files locally (automatically runs clean)
  • push: Push the generated files to the remote server. This uses scp by default.
  • new: Generate a new Markdown document in _posts. Pass TOPIC="foo" to change the name of the generated post (new article by default).

To use this Makefile with Jekyll, just reconfigure the REMOTEHOST and REMOTEDIR parameters at the bottom of the file with your own settings.

deploy: build push

help:
	@echo "You may provide several parameters, like:"
	@echo "make [target] KEY=\"value\""
	@echo ""
	@echo "The following parameters are available (with the defaults): "
	@echo "REMOTEHOST=$(REMOTEHOST)"
	@echo "REMOTEDIR=$(REMOTEDIR)"
	@echo ""
	@echo "You may provide the TOPIC variable to the 'new' target."
	@echo ""

.PHONY: clean
clean:
	rm -rf _site/*

push:
	scp -r _site/* $(REMOTE)
	growlnotify -m "BLOG: Uploaded."

build: clean
	jekyll
	chmod -R 755 _site/*
	growlnotify -m "BLOG: Built."

serve: clean
	jekyll --server

new:
	echo "---" >> $(FILE)
	echo "title: $(TOPIC)" >> $(FILE)
	echo "layout: post" >> $(FILE)
	echo "published: false" >> $(FILE)
	echo "---" >> $(FILE)
	open $(FILE)

# Change these settings for your own use, for example:
# REMOTEHOST ?= yourwebsite.com
# REMOTEDIR ?= /path/to/your/webroot
# Note the lack of a / on webroot
REMOTEHOST ?= yakko
REMOTEDIR ?= ~/grahamc.com/main/public
REMOTE = $(REMOTEHOST):$(REMOTEDIR)

TOPIC ?= new article
FILE = $(shell date "+./_posts/%Y-%m-%d-$(TOPIC).markdown" | sed -e y/\ /-/)

This Makefile also utilizes the Mac program Growl. If you don’t use a Mac (or don’t have Growl, remove the growlnotify lines.)

comments · posted on December 25 2010

Testing Symfony and Propel With PHPUnit

Running PHPUnit alongside of symfony is a fairly easy matter. Including the ProjectConfiguration file initializes the autoloader, your libraries get set up, everything is gravy… Except when its not.

When you get into contexts, configurations, applications, database integration, or nearly anything else that touches even the basic symfony bits, you drag in the whole symfony stack. Something in the stack caused every test to throw an unintelligible RuntimeException, with thousands of literal question marks (read: not an encoding error.)

Even through modifying symfony and PHPUnit’s sourcecode, running XDebug, not running XDebug, etc. nothing could convert those question marks into real-life, intelligible errors. Imagine how silly I felt searching Google for ”phpunit symfony question marks” - nothing, as you could imagine, came up.

I had worked through both of the existing Symfony plugins, sfPHPUnitPlugin and sfPHPUnit2Plugin. One of the is very over-arching and depends on an old version of PHPUnit (however it did work) and the newer one gave me the same issue.

After a long time of organized debugging, I devolved (as one usually does) to a series of shotgun debugging.

How I Solved It

Firstly, you’re going to need to use a bootstrap file to initialize the context. Basic, cut and dry:

<?php
$path = realpath(dirname(__FILE__) . '/../config/');
require_once $path . '/ProjectConfiguration.class.php';

// Initialize the application
$m = ProjectConfiguration::getApplicationConfiguration('frontend',
                                                       'testing', true);

// Now initialize the database bits
sfContext::createInstance($m);
new sfDatabaseManager($m);

error_reporting(E_ALL | E_STRICT);
ini_set('display_errors', true);
?>

Drop this into test/bootstrap.php. Every time you run phpunit from here, you pass the --bootstrap test/bootstrap.php

<phpunit
    colors="true"
    verbose="true"
    convertErrorsToExceptions="true"
    convertNoticesToExceptions="true"
    convertWarningsToExceptions="true"
    stopOnFailure="true"
    processIsolation="false"
    syntaxCheck="true"
    bootstrap="test/bootstrap.php">
    
    <testsuites>
        <testsuite name="Unit Tests">
            <directory>test/unit/</directory>
        </testsuite>
    </testsuites>

    <filter>
        <blacklist>
            <directory suffix=".php">lib/vendor</directory>
            <directory suffix=".php">cache</directory>
            <exclude>
                <directory suffix=".php">lib/vendor</directory>
            </exclude>
        </blacklist>
    </filter>
</phpunit>

Typing in phpunit --bootstrap test/bootstrap.php is pretty ridiculous, so drop this XML into phpunit.xml.

That phpunit.xml will make it pretty and ensure to exclude the extraneous files which you don’t want included in any coverage data. It will also specify the bootstrap.php which you just created. At this stage you just execute phpunit and all should be well.

For the record, I never did discover the cause of the question marks. I did, discover, however, that the trick is to enable process isolation.

comments · posted on November 30 2010

Making Objects Linkable in symfony

While working on a new project, I was exhausted by all the link_to writing I had been doing. It was repetitive, and even though I was using proper object-based routes - it was still ugly. All of them were formatted the exact same way, it was just silly. Definitely not DRY.

I started refactoring this by making a helper to link to an object. It was specific - link_to_group(NFGroup $group), link_to_user(sfGuardUser $user) and about the third time I wrote a nearly identical helper, I wrote another helper - link_to_object(BaseObject $object, $route). This wasn’t pretty, and I wanted a simpler solution.

What I ended up with was an interface for objects that I wanted to be linkable. The interface provided a way to get the route, parameters for the route, and then used the __toString() method on the object for the link text.

<?php
/**
 * Makes an object easy to link to by passing it to the
 * linkableLink helper
 * 
 * @author Graham Christensen <graham@grahamc.com>
 */
interface NF_Linkable {
	/**
	 * Get the route
	 * @return string The route
	 */
	public function getRoute();
	
	/**
	 * Get route parameters
	 * @return array of route parameters
	 */
	public function getParameters();
	
	/**
	 * Get the text to use as link-text
	 * @return string
	 */
	public function __toString();
}
?>

The NF_Linkable interface, allowing it to be easily linked to in a symfony view. To use this, place it in lib/NF_Linkable.php

My new helper is named linkableLink(NF_Linkable $object) and it will output a link, nice and simply. The helper is fairly straightforward too:

<?php
/**
 * Link to any object extending NF_Linkable 
 * @param NF_Linkable $object
 * @return string
 * @author Graham Christensen <graham@grahamc.com>
 */
function linkableLink(NF_Linkable $object) {
	$url = $object->getRoute() . '?';
	$url .= http_build_query($object->getParameters());
	
	return link_to($object, $url);
}
?>

This is pretty straightforward. It takes the object’s route, builds the query string from the parameters, and passes it along to symfony’s link_to helper. For those following along, place this in lib/helpers/LinkableHelper.php

To use this is simple enough, using the example of a group’s Propel object - we simply implement the interface’s methods:

<?php

/**
 * A group object which is linkable using linkableLink
 * 
 * @author Graham Christensen <graham@grahamc.com>
 */
class FieldGroup extends BaseFieldGroup
						implements NF_Linkable {
	/**
	 * Get the route to show a group
	 * @return string
	 */
	public function getRoute() {
		return '@group_show';
	}
	
	/**
	 * Get the route's parameters for building the final URL
	 * @return array
	 */
	public function getParameters() {
		return array('id' => $this->getId());
	}
	
	/**
	 * Get the text to appear between in <a>text</a> tags
	 * @return string
	 */
	public function __toString() {
		return $this->getName();
	}
}
?>

This is an example of how I use the NF_Linkable interface. This would create a link to this particular group using the name as the link text. The URL generated would be something like @group_show?id=1, which

symfony translates into group/view/1

To then use this in your codebase, write a simple view:

<?php
// Get a FieldGroup just for the example
$group = FieldGroupPeer::doSelectOne(new Criteria()); 

use_helper('Linkable');
echo linkableLink($group);
?>
comments · posted on September 10 2010

Managing Remote SVN Branches in Git-SVN

This was a major problem I’ve been having with git-svn is how to handle remote branches. There is lots of documentation on SVN, switching to Git, using git-svn, but very little seemed to be related to creating remote branches and then switching to them.

Turns out it was pretty obvious:

To create the branch:

git svn branch foo

To switch to the branch:

git branch foo remotes/foo

To delete a remote branch you have to do it with SVN’s command line tool:

svn rm http://svn.foobar.org/branches/foo
comments · posted on August 25 2010

How to Setup sfPDOSessionStorage

Setting up sfPDOSessionStorage is a fairly simple matter to make sure that sessions exist on a setup with multiple web-heads.

Add the following code to your app/app_name/config/factories.yml file:

all:
    storage:
    class: sfPDOSessionStorage
    param:
      db_table:    session
      database:    propel
      # Optional parameters
      db_id_col:   sess_id
      db_data_col: sess_data
      db_time_col: sess_time

Make sure your remove all unnecessary references to setting a different storage mechanism

Now add the following YAML to your config/schema.yml file. This creates the table structure.

# Session
  session:
    _attributes: { phpName: Session }
    sess_id: { type: varchar, size: 64,
               required: true, primaryKey: true }
    sess_data: { type: longvarchar }
    sess_time: { type: INTEGER, size: '11'}
    _indexes: { SESSIONTIME: [sess_time] }

I try to keep the generated SQL as up to date as possible, so do that now.

./symfony cc
./symfony propel:build-sql

If you need to make this change to an existing dataaset, here is the raw SQL to create the table:

CREATE TABLE `sessions`
(
        `sess_id` VARCHAR(64)  NOT NULL,
        `sess_data` TEXT,
        `sess_time` INTEGER(11),
        PRIMARY KEY (`sess_id`),
        KEY `SESSIONTIME`(`sess_time`)
)Type=InnoDB;

Note: If the session table is MyISAM, you’re going to hurt yourself with table-level locking. Making it InnoDB means row-level locking, and much better performance. Also, this only scales so far - eventually memcache is the solution.

comments · posted on June 16 2010