JasonDaly.name

PHP, Ruby, Symfony, Rails, Doctrine, MooTools. Web Development.

Posts tagged with "code"

June 14, 2010

Doctrine 1.2 + Sluggable Template + Conditionally Overriding Default Options

Doctrine provides a stock Sluggable template behavior that can be applied to models, generating a slug based on one or more columns for a Doctrine_Record instance.

In most cases it is best practice not to modify a slug after it is generated, since one or more external sources may be referencing a URL to the Doctrine_Record instance by the original slug. If the slug is modified, those URLs referencing the original slug will become broken. For this reason, Doctrine’s default behavior is to have the canUpdate option for the Sluggable template be set to false.

There may however be reason to override this disabled canUpdate behavior under certain conditions. Doctrine 1.2 does not seem to provide an easy way to access a specific Doctrine_Record_Listener and modify an option associated with that listener for a given Doctrine_Record instance. Assuming there is only a single instance of any given listener associated with a Doctrine_Record, it’s fairly straightforward to find the desired listener within the Doctrine_Record_Listener_Chain. Applying this search conditionally to a Doctrine_Record::preUpdate() hook would look something like is shown below.

// In your Doctrine_Record class
public function preUpdate($event){
  if (true) { // Conditions to check against for whether or not to allow the slug to be modified go here
    try {
      SomeUtilityClass::getListener($event->getInvoker()->getListener(), 'Sluggable')->setOption('canUpdate', true);
    } catch (Exception $e) {
      // Silently fail
    }
  }
}


// In SomeUtilityClass
class SomeUtilityClass {

  static public function getListener(Doctrine_Record_Listener_Chain $chain, $search) {
    $i = 0;
    do {
      if ($chain[$i] == null) {
        throw new InvalidArgumentException('The requested Doctrine_Record_Listener is not associated with this Doctrine_Record instance');
      }
      if (get_class($chain[$i]) === sprintf('Doctrine_Template_Listener_%s', $search)) {
        return $chain[$i];
      }      
    } while (++$i);
  }

}

The above getListener() method is required since it appears the only access to Doctrine_Record_Listener is via an implementation of PHP’s predefined ArrayAccess interface. Since the Doctrine_Record_Listener_Chain class only implements the set() and get() methods from the ArrayAccess interface, the only way to access the listeners is via their automatically assigned numeric keys. There is no way to directly request a specific listener by name through the Doctrine 1.2 API, thus the necessity for the do...while loop.

Tags: php doctrine doctrine 1.2 doctrine_record sluggable behavior template listener code

June 7, 2010

Symfony’s format_date(), l10n Support, and Custom Formatting

Symfony comes with some great i18n and l10n support. The DateHelper comes with functionality for templates to provide locale-specific date formats. There are a bunch of default formats that can be easily used as

format_date(strtotime('now'), 'P', 'en_US'); // Outputs 'Monday 7 June 2010'
format_date(strtotime('now'), 'P', 'fr_FR'); // Outputs 'Lundi 7 Juin 2010'

Some of the pre-defined patterns are constructed using special token patterns. These tokens can be passed to the 2nd parameter of the format_date() function above to generate custom formatted locale-specific date/time stamps.

format_date(strtotime('now'), 'EEE, MMMM dd, yyyy hh:mm a'); // Outputs 'Mon, June 07, 2010 12:40AM'

It doesn’t appear detailed information is available in any of the main documentation books or in the source code regarding the tokens available for date/time formatting, but I did come across a wiki page in the symfony trac.

1 note Tags: php symfony tips format_date l10n code

May 13, 2010

Subversion: Merging from trunk to branch; Back to trunk

During a normal development cycle, multiple branches of trunk may exist simultaneously with different features/fixes being developed within each. In order to keep trunk relatively stable at all times, it is a good idea to merge any changes to trunk since the branch was created into that branch when the branch is ready to be pushed into trunk. Confusing? The process is as follows

  1. Create a branch
  2. Develop new feature/fix in the branch (other developers are doing the same in their respective branches)
  3. Once finished with the branch
    1. Merge changes to trunk since the branch was created into the branch
    2. Fix any conflicts/bugs that arise from the newly added functionality from the trunk changes
    3. Merge the completely resolved and tested branch back into trunk

This is by no means the only way to manage development of a new feature/fix within Subversion, and I make no claim that it is the best. This is simply what works for my team at work presently. The end of the process described above (starting with 3) looks as follows

$ cd /path/to/branch
$ svn log --stop-on-copy # Run this from within the branch root - notice the revision # when the log output stops
$ svn merge -r ###:HEAD /path/to/trunk . # Here ### is the revision number from above

# Resolve any conflicts and test all code again in the branch
# Now to get the changes from the branch merged successfully back into trunk

$ cd /path/to/trunk
$ svn merge /path/to/branch -r ###:HEAD --accept theirs-full
$ svn resolve -R --accept working *
$ svn ci -m "Merging branch ____ into trunk" # Commit the branch merge into trunk

The 2nd and 3rd from last lines above are the ones of interest; they are the ones responsible for getting the work back into trunk. These lines are really only necessary since we had first merged work from trunk into the branch. The first line

$ svn merge /path/to/branch -r ###:HEAD --accept theirs-full

merges all changes from the branch since it was created back into trunk. This includes all code merged from trunk to the branch, so we are guaranteed that the branch has the latest and greatest of all code. As such, we force SVN to resolve all conflicts by accepting the branch copy’s version of every file within our working trunk copy. The next line

$ svn resolve -R --accept working *

Resolves any tree conflicts that have been introduced.

Tags: svn subversion code development bash trunk branch merge resolve

May 8, 2010

Introducing SpellCheck

After learning Github is now offering SVN support (nearly all of my development work is done using SVN), I decided it was time to properly version my small changes to the great gRaphael library by forking the original code with my own account. I also decided to start to maintain smaller utilities I write for personal use through git on Github as well. The first of these packages is \Deefour\SpellCheck.

SpellCheck v1.0

Introducing ”SpellCheck v1.0”. As mentioned in the README,

SpellCheck leverages “…the XML request/response used by the Google Toolbar…” accepting “… a string to be transformed into a corrected version of itself.”

This class simply makes all corrections suggested by Google to the original string passed in. Admittedly, this is not as flexible as some will like, but for now it suits my needs and is a great start. Some small points:

Usage instructions and code can be found in the v1.0 Tag on GitHub.

Tags: php spellcheck releases v1.0 code github 5.3 php5.3

May 5, 2010

Shifting the Window to Display Anchors Below Fixed Element

When browsing to an anchor within an page, all browsers (that I know of) scroll the page to the anchored element such that the element is at the very top of the visible browser window. This is a problem when a site design includes a fixed element at the top of the browser window. A little bit of Javascript (via MooTools) can fix this:

var AnchorFix = new Class({

  Implements: Options,

  options: {
    fixed_element: null
  },

  initialize: function(options){
    this.setOptions(options);

    this.applyTrigger();
  },

  /**
   * Applies the onhashchange event to the window
   * to listen for a new anchor being browsed to by the user
   */
  applyTrigger: function(){
    var fn = window.onhashchange || $empty;
    window.onhashchange = function() {
        fn();
        this.reScrollWindow();
    }.bind(this);
  },

  /**
   * Calculates the proper scroll point for the window based
   * on the height of the fixed element at the top of the page,
   * allowing the anchored element to be visible under the fixed
   * element
   */
  reScrollWindow: function(){
    var hash = window.location.hash.trim().substring(1);
        var element = ($(hash) != null) ? $(hash) : $(document.body).getElement('*[name="' + hash + '"]');

        if (element != null) {
        var position_y = element.getPosition().y;
        var bar_height = $(this.options.fixed_element).getSize().y

        window.scrollTo(0, (position_y - bar_height - 5));
      }
  }
});

// Instantiate a new instance of the class to get started
var fixMyAnchors = new AnchorFix({$('idOfTheFixedElement')});

Not all browsers support the Javascript onhashchange event (read: IE7 and earlier). A bit of the applyTrigger() code above was modified from MooHistory, and with a bit of tweaking if desired, support could be added to the code above for earlier versions of IE (check out their source to see how they handle IE6/7).

Tags: Mootools browsers javascript css onhashchange code

April 30, 2010

MooTools’ .attempt(), arguments, and Javascript’s apply()

arguments is a local object variable available within any function in JavaScript. Arguments can be accessed as shown in foo() below.

function foo(){
    console.debug(arguments); // ['a', 'b']
    console.debug(arguments[0]); // 'a'
}

foo('a', 'b');

This is convenient for functions that may need to accept an arbitrary number of parameters. But if these arguments need to again be passed on to a 2nd function from within the first, a problem arises:

function foo(){
    // ...
    bar(arguments);
}

function bar(){
    console.debug(arguments[0]); // ['a', 'b']
    console.debug(arguments[1]); // undefined
    // ...
}

foo('a', 'b');

Notice that arguments[0] in bar() above now contains an array of the arguments passed to foo(), while arguments[1] is undefined. MooTools has an .attempt function prototype (See the documentation) that fixes this issue.

function foo(){
    // ...
    bar.attempt(arguments); # Call bar() using MooTools attempt()
}

function bar(){
    console.debug(arguments[0]); // 'a'
    console.debug(arguments[1]); // 'b'
    // ...
}

foo('a', 'b');

Looking at MooTools 1.3b1.1 Source, the arguments are passed to the requested function using apply() (See the documentation).

Tags: Mootools javascript code

March 24, 2010

MooTools toElement() - Using a Class Instance to Retrieve the Related DOM Element

In MooTools the $() is used in part to ensure the element passed to $() is extended by MooTools. Clientcide points out a great tip with the native Class toElement() function.

Aside from the benefits from the use of this from outside of a class passing an instance variable to $(), I like to use toElement() in combination with $(this) from within my Class objects simply for brevity.

var someClass = new Class({

  Implement: [Options],

  options: {
    domReference: null
  },

  initialize: function(options){
    this.setOptions(options);

    // Here, $(this) will reference this.options.domReference :)
    $(this).setStyle('background-color', '#F00');
  }

  toElement: function(){
    return this.options.domReference;
  }
});

var someInstance = new someClass({
  domReference: $('some-dom-element-id')
});

Obviously the example is a little bit ridiculous just to style an element’s background red, but the point is for me it’s much cleaner having the option to write

$(this).setStyle('background-color', '#F00');

than

this.options.domReference.setStyle('background-color', '#F00');

Tags: mootools javascript code tips

April 3, 2009

Aiding the SVN Commit Process

The source code for D4core is maintained in and SVN repository. I have found it helpful to create a .cleanup and .commit Bash script in the root directory of our framework’s repository.

.cleanup has the task of removing cached files, temporary files, and log files which are no longer relevant to the current development cycle. It also use it currently to reformat our XML configuration file with pretty whitespace using xmllint.

#!/bin/bash

echo "Cleaning D4core..."
rm -f D4core/Content/Cache/Smarty/*
rm -f D4core/Content/Compile/Smarty/*
rm -f D4core/Content/Cache/Minify/*
rm -f D4core/Config/Backup_*
rm -f D4core/Log/Error/*
rm -f D4core/Log/Logs/*

xmllint -format D4core/Config/Config.xml -output D4core/Config/Config.xml
echo 'Running `svn status`...'
svn status
echo "Cleaning D4core completed."

.commit (which calls .cleanup initially) is responsible for managing the commit process. This file is run only after all project files have been added/removed/moved within the working copy and we are ready to commit to SVN.

#!/bin/bash
./.cleanup
svn commit
echo "D4core SVN commit complete."

By running ./.cleanup before committing code to the repository it becomes a lot easier to ensure the status and location of all files within the working copy are correct. These simply scripts easily save me 5 minutes a day in shell.

Tags: bash code d4core svn subversion linux xmllint

March 25, 2009

Clear /.svn Directories From a Working Copy

Below is one way to quickly and simply clear all /.svn directories from a SVN working copy; useful in a variety of situations.

find . -type d -name .svn -exec rm -rf '{}' ; -print 2> /dev/null

Tags: svn bash code tips subversion linux

February 25, 2009

Making AppleTV Encoded Video Files PlayStation 3 Compatible

Using Handbrake to rip DVDs, I use a slight customization of the default AppleTV encoding settings. I find the quality-to-filesize ratio is great for nearly zero setup time. But when trying to play the encoded .mp4 video files on my PlayStation 3, the video format was unrecognized.

Running this1 over a movie.mp4 AppleTV encoded movie fixes this issue, while still maintaining compatibility with AppleTV.

perl -pi.bak -e "s|(avcC.{3}).(.{7}).|1x292x29|" movie.mp4

In the case where I have ripped one of my DVDs of TV show episodes and am left with multiple .mp4 files requiring this fix, I run this script from within the folder

#!/usr/bin/bash

for file in `find . -type f -name *.mp4`;
do
    perl -pi.bak -e "s|(avcC.{3}).(.{7}).|1x292x29|" $file;
done;

  1. Here is the link to the original forum thread where I found this information. Thanks, Paul! 

Tags: appletv dvd encoding apple bash perl playstation ps3 code mp4 tv handbrake mac