read

Yahoo r3 is a powerful tool for building websites which could require multiple

languages or dimensions. The r3 outputs may be plain text files, web templates, configuration files or even source code. Sometimes maintaining a big site translated into different languages can become a nightmare, but r3 lets you do it on an easy way. It offers a neat and powerful command line interface which will make your life easier. It can also be managed by using a web interface.

I know this could be a somewhat confusing thing to explain, so I’m going to try to give an example of what you can do with it. At Weblogs SL, we have a platform which handles instances of 37 blogs in 2 languages (Spanish and Portuguese), so we have to maintain 37×2 = 74 sites. They almost share the same layout

structure, but sometimes we need to make specific changes on certain site (maybe a customized header or sidebar). r3 lets you do all this kind of things by specializing templates for a particular site/language. Coming back to our example, we have two dimensions: blog and language. Once we have created the dimensions, we can generate all the templates. These raw templates are parseable by the template engine included within our platform, so they are processed by the application afterwards. We can also put all the “static variables” for each blog (such as blog titles, URLs, paths, …) during this pre-build process.

So after far too much research, we came up with Yahoo r3, but we still had an itch to scratch: how can we integrate this into our continuous integration workflow? We are using Phing (and Java Ant) for building our sites, so it was nice to have some Phing tasks to work with r3. So I wrote these two tasks:

r3GenerateTask.php

require_once 'phing/Task.php';
/**
 * r3 Generate Task
 *
 * PHP version 5
 *
 * @category  Phing Task
 * @package   phing
 * @author    Alfonso Jimenez
 * license   http://www.gnu.org/licenses/gpl.html GPL

 *
 */

class r3GenerateTask extends Task
{
    /**
     * path to r3 workspace
     *
     * var  string
     */

    protected $r3Workspace;

    /**
     * init method
     *

     * @return boolean
     */

    public function init()
    {
        return true;
    }

    /**
     * main method
     *
     * @return void
     */

    public function main()
    {
        $command = 'r3 -c '. $this->Workspace .' generate -av';
        $output  = array();
        $return  = null;

        try {
            exec($command, $output, $return);

            foreach ($output as $line) {
                $this->log($line, Project::MSG_VERBOSE);
            }

            if ($return !== 0) {
                throw new BuildException('Task exited with code'. $return);
            }
        } catch (BuildException $e) {

            $op = Project::MSG_WARN;

            if ($this->quiet === true) {
                $op = Project::MSG_VERBOSE;
            }

            $this->log($e->getMessage(), $op);
        }
    }
}

r3SetVarTask.php

require_once 'phing/Task.php';

/**
 * r3 setVar Task
 *
 * PHP version 5
 *
 * category Phing Task
 * package phing
 * author Alfonso Jimenez <[email protected]>
 * license http://www.gnu.org/licenses/gpl.html GPL
 *
 */
class r3SetVarTask extends Task {
    /**
     * path to r3 workspace
     *
     * var string
     */
    protected $r3Workspace;

    /**                                             
     * dimension                                     
     *                                              
     * @var string                                   
     */                                             
    protected $dimension;                           

    /**                                             
     * variable key                                 
     *                                               
     * @var string                                   
     */                                             
    protected $key;                                 

    /**                                             
     * variable value                               
     *                                               
     * @var string                                   
     */                                             
    protected $value;                               

    /**                                             
     * sets the r3 workspace path                   
     *                                               
     * param string $r3Workspace                   
     *                                               
     * return void                                 
     */                                             
    public function setr3Workspace($r3Workspace)     
    {                                               
        $this->r3Workspace = $r3Workspace;           
    }                                               

    /**                                             
     * sets dimension                               
     *                                               
     * param string $dimension                     
     *                                               
     * return void                                 
     */                                             
    public function setDimension($dimension)         
    {                                               
        $this->dimension = $dimension;               
    }                                               

    /**                                             
     * sets the variable key                         
     *                                               
     * param string $key                           
     *                                               
     * return void                                 
     */                                             
    public function setKey($key)                     
    {                                               
        $this->key = $key;                           
    }                                               

    /**                                             
     * sets the variable value                       
     *                                               
     * param string $value                         
     *                                               
     * return void                                 
     */                                             
    public function setValue($value)                 
    {                                               
        $this->value = $value;                       
    }                                               

    /**
     * init method
     *           
     * @return boolean
     */             
    public function init()
    {                   
        return true;     
    }                   

    /**                 
     * main method       
     *                   
     * @return void     
     */                 
    public function main()
    {                   
        $command = ‘r3 -c ‘. $this‐>r3Workspace .’ var set ‘. $this‐>dimension
                   .’ ‘. $this‐>key .’ ‘. $this‐>value;                     
        $output  = array();                                                 
        $return  = null;                                                     

        try {                                                               
            exec($command, $output, $return);                               

            foreach ($output as $line) {                                     
                $this->log($line, Project::MSG_VERBOSE);
            }

            if ($return !== 0) {
                throw new BuildException(‘Task exited with code’. $return);
            }
        } catch (BuildException $e) {

            $op = Project::MSG_WARN;

            if ($this->quiet === true) {
                $op = Project::MSG_VERBOSE;
            }

            $this->log($e->getMessage(), $op);
        }
    }
}
If you want to use these tasks in your Phing file, you have to place them in /usr/share/php/phing/extended/tasks/ first. Then you can call them by writing the following tag-commands:

<taskdef name=”r3-generate” classname=”extended.tasks.r3GenerateTask” />
<taskdef name=”r3-setVar” classname=”extended.tasks.r3SetVarTask” />

<r3-generate r3Workspace=”${workspace.dir}”></r3>
<r3-setVar r3Workspace=”${workspace.dir}” dimension=”${dimension}” key=”var_key” value=”${var_value}”></r3>

Let’s see an example of a real Phing file. Imagine that we have to write a task which needs to do the following steps:

  1. Get a Yahoo r3 workspace from a SVN repository (http://svn.test.com/trunk/r3)
  2. Build the Yahoo r3 workspace
  3. Move the generated templates into a certain directory
  4. Clean up the temporary files

So we can straightforward write a Phing task in order to follow this sequence of steps:

<?xml version=“1.0”?>
<project name=“Yahoo r3 Templates Build” basedir=“.” default=“build”>

   <property name=“tmp.dir” value=“/tmp/template” />
   <property name=“templates.dir” value=“/home/r3/templates” />
   <property name=“svn” value=“http://svn.test.com/trunk/r3” />

   <target name=“build” depends=“init, generate, clean” />

   <target name=“init”>
      <taskdef name=“r3-generate” classname=“extended.tasks.r3GenerateTask” />

      <mkdir dir=“${tmp.dir}” />
      <svncheckout svnpath=“/usr/bin/svn” repositoryurl=“${svn}” todir=“${tmp.dir}”/>
   </target>

   <target name=“generate”>
      <r3-generate r3Workspace=“${tmp.dir}”></r3-generate>
   </target>

   <target name=“clean”>
      <copy todir=“${templates.dir}”>
        <fileset dir=“${tmp.dir}/htdocs”>
           <include name=“**” />
        </fileset>
      </copy>

      <delete dir=“${tmp.dir}” includeemptydirs=“true” failonerror=“true” />
   </target>
</project>
Blog Logo

Alfonso Jiménez

Software Engineer at Jobandtalent


Published

Image
Back to Overview