À moi la garde !

  drupal, behat, drupal-7

In November 2013, I tried Drupal BDD using Behat and the Behat Drupal extension. The experiment was to apply BDD in order to implement a small feature on an existing site. After a informal discussion about the feature, the stakeholder wanting it wrote the Cucumber scenarios. As the developer of the feature, I took the scenarios and started by running them against my test site. And so, my BDD journey started.

The first thing that needed fixing was the scenario itself, off course being the first scenario ever written by its author, most of the step, while understandable by an human being did not match the pattern expected by Behat (actually the (Mink)[http://mink.behat.org/] and Drupal extensions). While fixing the steps, I quickly learned that steps to easily express the visibility of Drupal form element did not exists. So I wrote a couple or steps to check for the (in)visibility of form element identified by their labels. Writing the steps was the occasion to dig the Behat and Mink APIs. Most of them requires a (Mink driver)[http://mink.behat.org/#different-browsers-drivers] with support for CSS in order for the visibility tests on the form element to work. The Goutte driver does not, the Selenium driver does not. I don't known for others.

The code for these steps is distributed under a MIT-style licence as Gist (embed below). In (issue 2151935)[https://drupal.org/node/2151935] Another Behat/Drupal enthusiast requested for them to be included . This would require a bit more work as they currently heavily rely on, what I think is, the default Drupal form markup and have only been tested in single custom theme. Because we can expect everyone to use default form markup, the code to retrieve a form element or its label should abstracted to easily overwriteable methods. One of these methods should be used to retrieve the form labels on the page. Once you have a form label element, you can retrieve its form element using its for attribute (requiring this attribute is a reasonable requirements). Another method should be used to retrieve the type (ie. the Drupal form API #type) of a form element.

  nes, digispark, arduino

The Digispark is a tiny Arduino compatible (with restrictions) USB enabled development board. A while ago I baked their Kickstarter campaign but didn't made anything with my Digisparks until this week. I finally tried to connect one to another item I bough long ago, an original NES controller.

Consuming a NES controller with an Arduino is actually easy, a quick googling returns tons of working code. There is even an Arduino library which looks like it could work on a Digispark. Exposing the the Digispark as an USB Joystick is even easier, thanks to the DigiJoystick library which is itself based on the well known V-USB (if you ever googled the "arduino usb" keywords, then you've already encountered v-usb).

I ended with the following code, which seems to do the job. Unfortunately, my NES controller revealed itself to be broken, the left button is always seen as pressed except when down is pressed.

#include "DigiJoystick.h"

#define LATCH   1
#define CLOCK   2
#define DATA    5
#define UP      B00001000
#define DOWN    B00000100
#define LEFT    B00000010
#define RIGHT   B00000001
#define BUTTONS B11110000

byte state = 0;

void setup() {
  pinMode(LATCH,OUTPUT);
  pinMode(CLOCK,OUTPUT);
  pinMode(DATA,INPUT);

  digitalWrite(LATCH,HIGH);
  digitalWrite(CLOCK,HIGH);
}

void loop() {
  // Read controller's state
  state = 0;
  digitalWrite(LATCH,LOW);
  digitalWrite(CLOCK,LOW);
  digitalWrite(LATCH,HIGH);
  delayMicroseconds(2);
  digitalWrite(LATCH,LOW);
  state = ~digitalRead(DATA);
  for (int i = 1; i <= 7; i ++) {
    digitalWrite(CLOCK,HIGH);
    delayMicroseconds(2);
    state = state << 1;
    state = state + ~digitalRead(DATA) ;
    delayMicroseconds(4);
    digitalWrite(CLOCK,LOW);
  }
  // set USB Joystick state.
  DigiJoystick.setY((byte)127);
  if (state & UP) {
    DigiJoystick.setY((byte)0);
  }
  if (state & DOWN) {
    DigiJoystick.setY((byte)255);
  }
  DigiJoystick.setX((byte)127);
  if (state & LEFT) {
    DigiJoystick.setX((byte)0);
  }
  if (state & RIGHT) {
    DigiJoystick.setX((byte)255);
  }
  DigiJoystick.setButtons((char)((states>>4) & BUTTONS), (char) 0);
  DigiJoystick.delay(10);
}

  drupal, drupal-7, javascript, jquery, drupalcamp, presentation

On October 26th, at the the Drupal Camp Montréal, I made a presentation on the usage of JavaScript in the context of a Drupal site. I think the presentation went fine, and it seems a few people in the audience enjoyed and, more importantly, learned something out of it.

The presentation is the result of all the feedbacks I got on the first public version of my previous post, originally written internally at Phéromone and then shared on corderwall. Since all the developers working with Drupal who read it told nme they learned something from it, I figured way too much developers don't known about what I think are the basics of working with JavaScript in Drupal.

The slides for this presentation are, since a few weeks, available online at http://pbuyle.github.io/dcmtl-javascript.

  drupal, drupal-7, javascript, jquery

tl;dr: JavaScript code to process elements on page load on a Drupal 7 site should looks like this:

(function($) {
  Drupal.behaviors.doSomething = {
    attach: function(context, settings) {
      $('div.something', context).once('do-something').doSomething({
        param1: settings.somethingl.param,
        param2: 'something else'
      });
    }
  }
})(jQuery);

Drupal 7 provides jQuery in the no-conflict mode, which means that $ is not the jQuery object/namespace. This should not be an issue with properly written jQuery plugins that follow jQuery's plugins authoring documentation. This is however an issue for code snippets mindlessly copy/pasted from random web pages. Most of them expect $ to be the jQuery namespace and will not work within a Drupal page. This can be easily solved by wrapping theses snippets in immediately invoked anonymous function that will alias the jQuery namespace to $:

(function($) {
    // Here $ is the jQuery namespace.
})(jQuery);

Usually, JavaScript code that needs to run at page load, is also wrapped in a function passed as argument to jQuery() or jQuery(document).ready():

$(function() {
  // Code here is executed when the DOM is loaded.
});

When combined, these two patterns are perfectly fine, even within Drupal. However if content (ie. new DOM elements) is added to the page after page load (AJAX calls, content generated from JavaScript, etc.) the code in such functions will never be able to process the added elements. Or if some portion of the content is removed or moved across the page, the code will have no option to unregistered event handlers or update information about the already processed elements. Drupal provides an API for this called behaviors. Using behavior is not required, but strongly recommended as a best practice to avoid future headaches (when code written six months ago starts behaving strangely when a contrib module is added to the project). A behavior is written like this:

Drupal.behaviors.behaviorName = {
  attach: function (context, settings) {
    // Do something.
  },
  detach: function (context, settings, trigger) {
    // Undo something.
  }
};

The attach function of all registered behaviors (all properties of the Drupal.behaviors object) will be invoked when behavior should be added to elements, either when the DOM is ready (ie. page load) and when elements are added to the DOM. The detach function will be called when behaviors should be detached from elements: just before elements are removed from the DOM, moved in the DOM or a form is submitted. The context parameter will always be a parent of the added elements, the single added/removed/moved/submitted element itself or the whole document element. The settings parameters will be the settings for the context, usually the Drupal.settings object as set by calls to drupal_add_js() from PHP. For detach, the trigger parameter will contains the kind of event that triggered the call: 'unload' (elements removed), 'move' (elements moved) or 'serialize' (form is being submitted).

The attach (and detach) functions of a behavior can be used multiple time over the same portion of the DOM tree. So the same element could be processed multiple time by the same code. It is up to the code itself to avoid processing (ie. binding event handlers, altering CSS styles, etc.) multiple times for the same elements. The easiest solution for this is to use the jQuery Once plugin (which is provided by Drupal 7) like this:

$(selector).once('behavior-name').doSomething();
$(selector).once('behavior-name', function(){ /*do something*/ });

Since a behavior is being attached/detached to/from a context, the context object can be used to restrict your jQuery queries to only the affected element or DOM subtree, like this:

$(selector, context).doSomething();

Putting all this together means the base pattern to process elements on page load should looks like this:

(function($) {
  Drupal.behaviors.doSomething = {
    attach: function(context, settings) {
      $('div.something', context).once('do-something').doSomething({
        param1: settings.somethingl.param,
        param2: 'something else'
      });
    }
  }
})(jQuery);

References:

Welcome to my humble blog. It merely exists as a place where I hope to share some of the experiences and knowledge gathered developing software, mostly for web sites and mostly with Drupal.