<?xml version="1.0" encoding="utf-8"?><?xml-stylesheet type="text/xml" href="/feed.xslt.xml"?><feed xmlns="http://www.w3.org/2005/Atom"><generator uri="http://jekyllrb.com" version="3.2.1">Jekyll</generator><link href="/atom.xml" rel="self" type="application/atom+xml" /><link href="/" rel="alternate" type="text/html" /><updated>2016-09-06T21:21:24-04:00</updated><id>/</id><author><name>Ben Clark</name></author><entry><title type="html">Chef cookbook for Drush</title><link href="/articles/chef-cookbook-for-drush.html" rel="alternate" type="text/html" title="Chef cookbook for Drush" /><published>2013-10-20T00:00:00-04:00</published><updated>2013-10-20T00:00:00-04:00</updated><id>/articles/chef-cookbook-for-drush</id><content type="html" xml:base="/articles/chef-cookbook-for-drush.html">&lt;p&gt;I attended Opscode Chef training a couple weeks ago and got super-psyched about it. I’m working on a project for &lt;a href=&quot;http://www.jacksonriver.com&quot;&gt;Jackson River&lt;/a&gt;, and utilizing Chef is a big component. While writing the cookbook for this project, I found that I needed more access to &lt;a href=&quot;https://github.com/drush-ops/drush&quot;&gt;Drush&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;At first, I used the &lt;code class=&quot;highlighter-rouge&quot;&gt;execute&lt;/code&gt; resource, but I couldn’t reliably make the commands idempotent. Furthermore, using &lt;code class=&quot;highlighter-rouge&quot;&gt;execute&lt;/code&gt; felt like a hack, and it made my recipes look less elegant. I decided to write custom LWRPs to extend the &lt;a href=&quot;https://github.com/msonnabaum/chef-drush&quot;&gt;existing Drush cookbook&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Here’s the result of that effort, &lt;a href=&quot;https://github.com/benclark/chef-drush&quot;&gt;my fork of the Drush cookbook&lt;/a&gt;: &lt;strong&gt;&lt;a href=&quot;https://github.com/benclark/chef-drush&quot;&gt;https://github.com/benclark/chef-drush&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;I’m openly soliciting feedback here. I’m a PHP developer, and this is the first Ruby code I’ve ever written. I cribbed from other cookbooks that attempt to do the same thing (borrowing quite a bit from the &lt;a href=&quot;https://github.com/opscode-cookbooks/jenkins/tree/master/providers&quot;&gt;Jenkins cookbook providers&lt;/a&gt;), and I did my best to be consistent and logical.&lt;/p&gt;

&lt;hr /&gt;

&lt;h2 id=&quot;drush-lightweight-resources&quot;&gt;drush Lightweight Resources&lt;/h2&gt;

&lt;p&gt;The &lt;strong&gt;drush&lt;/strong&gt; cookbook is used to interact with Drupal via Drush. It contains the following lightweight resources:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;#drush_cmd&quot;&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;drush_cmd&lt;/code&gt;&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;#drush_variable&quot;&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;drush_variable&lt;/code&gt;&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;#drush_php_eval&quot;&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;drush_php_eval&lt;/code&gt;&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;#drush_make&quot;&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;drush_make&lt;/code&gt;&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;#drush_site_install&quot;&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;drush_site_install&lt;/code&gt;&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;There is also a &lt;a href=&quot;#helper_library&quot;&gt;helper library&lt;/a&gt;.&lt;/p&gt;

&lt;h3 id=&quot;drushcmd&quot;&gt;drush_cmd&lt;/h3&gt;

&lt;p&gt;The &lt;strong&gt;drush_cmd&lt;/strong&gt; lightweight resource provides direct access to Drush. This is the backbone upon which the other resources interact with Drush.&lt;/p&gt;

&lt;h4 id=&quot;actions&quot;&gt;Actions&lt;/h4&gt;

&lt;p&gt;This lightweight resource provider has the following actions:&lt;/p&gt;

&lt;table class=&quot;table&quot;&gt;
  &lt;thead&gt;
    &lt;tr&gt;
      &lt;th&gt;Action&lt;/th&gt;
      &lt;th&gt;Description&lt;/th&gt;
    &lt;/tr&gt;
  &lt;/thead&gt;
  &lt;tbody&gt;
    &lt;tr&gt;
      &lt;td&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;:execute&lt;/code&gt;&lt;/td&gt;
      &lt;td&gt;Default. Indicates that the Drush command should be executed.&lt;/td&gt;
    &lt;/tr&gt;
  &lt;/tbody&gt;
&lt;/table&gt;

&lt;h4 id=&quot;attributes&quot;&gt;Attributes&lt;/h4&gt;

&lt;p&gt;This lightweight resource provider has the following attributes:&lt;/p&gt;

&lt;table class=&quot;table&quot;&gt;
  &lt;thead&gt;
    &lt;tr&gt;
      &lt;th&gt;Attribute&lt;/th&gt;
      &lt;th&gt;Kind&lt;/th&gt;
      &lt;th&gt;Description&lt;/th&gt;
    &lt;/tr&gt;
  &lt;/thead&gt;
  &lt;tbody&gt;
    &lt;tr&gt;
      &lt;td&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;command&lt;/code&gt;&lt;/td&gt;
      &lt;td&gt;String&lt;/td&gt;
      &lt;td&gt;&lt;strong&gt;Required.&lt;/strong&gt; The command sent to Drush. Examples: &lt;code class=&quot;highlighter-rouge&quot;&gt;pm-enable&lt;/code&gt;, &lt;code class=&quot;highlighter-rouge&quot;&gt;pm-disable&lt;/code&gt;, &lt;code class=&quot;highlighter-rouge&quot;&gt;site-install&lt;/code&gt;.&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;arguments&lt;/code&gt;&lt;/td&gt;
      &lt;td&gt;String, Array&lt;/td&gt;
      &lt;td&gt;The arguments to go with the command. Examples: for &lt;code class=&quot;highlighter-rouge&quot;&gt;pm-enable&lt;/code&gt; command, &lt;code class=&quot;highlighter-rouge&quot;&gt;devel&lt;/code&gt; as argument.&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;options&lt;/code&gt;&lt;/td&gt;
      &lt;td&gt;String, Array&lt;/td&gt;
      &lt;td&gt;The options to go with the command. Examples: for &lt;code class=&quot;highlighter-rouge&quot;&gt;vset&lt;/code&gt; command, &lt;code class=&quot;highlighter-rouge&quot;&gt;--exact&lt;/code&gt; option.&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;drupal_root&lt;/code&gt;&lt;/td&gt;
      &lt;td&gt;String&lt;/td&gt;
      &lt;td&gt;&lt;strong&gt;Required.&lt;/strong&gt; Equivalent to &lt;code class=&quot;highlighter-rouge&quot;&gt;drush -r &amp;lt;path&amp;gt;&lt;/code&gt;, &lt;code class=&quot;highlighter-rouge&quot;&gt;--root=&amp;lt;path&amp;gt;&lt;/code&gt;.&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;drupal_uri&lt;/code&gt;&lt;/td&gt;
      &lt;td&gt;String&lt;/td&gt;
      &lt;td&gt;Equivalent to &lt;code class=&quot;highlighter-rouge&quot;&gt;drush -l &amp;lt;http://example.com:8888&amp;gt;&lt;/code&gt;, &lt;code class=&quot;highlighter-rouge&quot;&gt;--uri=&amp;lt;http://example.com:8888&amp;gt;&lt;/code&gt;.&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;assume_yes&lt;/code&gt;&lt;/td&gt;
      &lt;td&gt;Boolean&lt;/td&gt;
      &lt;td&gt;Equivalent to &lt;code class=&quot;highlighter-rouge&quot;&gt;drush -y&lt;/code&gt;, &lt;code class=&quot;highlighter-rouge&quot;&gt;--yes&lt;/code&gt;. Defaults to &lt;code class=&quot;highlighter-rouge&quot;&gt;true&lt;/code&gt; because &lt;code class=&quot;highlighter-rouge&quot;&gt;chef-client&lt;/code&gt; can’t accept user input.&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;assume_no&lt;/code&gt;&lt;/td&gt;
      &lt;td&gt;Boolean&lt;/td&gt;
      &lt;td&gt;Equivalent to &lt;code class=&quot;highlighter-rouge&quot;&gt;drush -n&lt;/code&gt;, &lt;code class=&quot;highlighter-rouge&quot;&gt;--no&lt;/code&gt;. Defaults to &lt;code class=&quot;highlighter-rouge&quot;&gt;false&lt;/code&gt;.&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;backend&lt;/code&gt;&lt;/td&gt;
      &lt;td&gt;Boolean&lt;/td&gt;
      &lt;td&gt;Equivalent to &lt;code class=&quot;highlighter-rouge&quot;&gt;drush --backend&lt;/code&gt;. Defaults to &lt;code class=&quot;highlighter-rouge&quot;&gt;false&lt;/code&gt;.&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;shell_input&lt;/code&gt;&lt;/td&gt;
      &lt;td&gt;String&lt;/td&gt;
      &lt;td&gt;Optionally pipe a string into the Drush command.&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;shell_timeout&lt;/code&gt;&lt;/td&gt;
      &lt;td&gt;Integer&lt;/td&gt;
      &lt;td&gt;Set a timeout for the shell operation. Defaults to 900 (15 minutes).&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;shell_user&lt;/code&gt;&lt;/td&gt;
      &lt;td&gt;User&lt;/td&gt;
      &lt;td&gt;Run the Drush command as a specific user.&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;shell_group&lt;/code&gt;&lt;/td&gt;
      &lt;td&gt;Group&lt;/td&gt;
      &lt;td&gt;Run the Drush command as a member of a specific group.&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;block&lt;/code&gt;&lt;/td&gt;
      &lt;td&gt;Proc&lt;/td&gt;
      &lt;td&gt;Ruby block to act on the &lt;code class=&quot;highlighter-rouge&quot;&gt;stdout&lt;/code&gt; from the Drush command.&lt;/td&gt;
    &lt;/tr&gt;
  &lt;/tbody&gt;
&lt;/table&gt;

&lt;h4 id=&quot;examples&quot;&gt;Examples&lt;/h4&gt;

&lt;p&gt;To run &lt;code class=&quot;highlighter-rouge&quot;&gt;drush site-install&lt;/code&gt; for &lt;code class=&quot;highlighter-rouge&quot;&gt;example.com&lt;/code&gt;:&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-ruby&quot; data-lang=&quot;ruby&quot;&gt;&lt;span class=&quot;n&quot;&gt;drush_cmd&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;site-install&quot;&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;arguments&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;standard&quot;&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;options&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;--account-mail=example@example.com&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;--account-name=admin&quot;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;drupal_root&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;/var/www/html&quot;&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;drupal_uri&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;http://www.example.com&quot;&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;shell_user&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;www-data&quot;&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;shell_group&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;www-data&quot;&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;h3 id=&quot;drushvariable&quot;&gt;drush_variable&lt;/h3&gt;

&lt;p&gt;The &lt;strong&gt;drush_variable&lt;/strong&gt; lightweight resource provides the ability to set and delete variables in a given Drupal install.&lt;/p&gt;

&lt;h4 id=&quot;actions-1&quot;&gt;Actions&lt;/h4&gt;

&lt;p&gt;This lightweight resource provider has the following actions:&lt;/p&gt;

&lt;table class=&quot;table&quot;&gt;
  &lt;thead&gt;
    &lt;tr&gt;
      &lt;th&gt;Action&lt;/th&gt;
      &lt;th&gt;Description&lt;/th&gt;
    &lt;/tr&gt;
  &lt;/thead&gt;
  &lt;tbody&gt;
    &lt;tr&gt;
      &lt;td&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;:set&lt;/code&gt;&lt;/td&gt;
      &lt;td&gt;Default. Set a value for a given variable.&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;:delete&lt;/code&gt;&lt;/td&gt;
      &lt;td&gt;Delete a variable. (@todo)&lt;/td&gt;
    &lt;/tr&gt;
  &lt;/tbody&gt;
&lt;/table&gt;

&lt;h4 id=&quot;attributes-1&quot;&gt;Attributes&lt;/h4&gt;

&lt;p&gt;This lightweight resource provider has the following attributes:&lt;/p&gt;

&lt;table class=&quot;table&quot;&gt;
  &lt;thead&gt;
    &lt;tr&gt;
      &lt;th&gt;Attribute&lt;/th&gt;
      &lt;th&gt;Kind&lt;/th&gt;
      &lt;th&gt;Description&lt;/th&gt;
    &lt;/tr&gt;
  &lt;/thead&gt;
  &lt;tbody&gt;
    &lt;tr&gt;
      &lt;td&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;name&lt;/code&gt;&lt;/td&gt;
      &lt;td&gt;String&lt;/td&gt;
      &lt;td&gt;&lt;strong&gt;Required.&lt;/strong&gt; The name of the variable. Examples: &lt;code class=&quot;highlighter-rouge&quot;&gt;preprocess_css&lt;/code&gt;, &lt;code class=&quot;highlighter-rouge&quot;&gt;site_offline&lt;/code&gt;.&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;value&lt;/code&gt;&lt;/td&gt;
      &lt;td&gt;String, Integer, Hash, TrueClass, FalseClass&lt;/td&gt;
      &lt;td&gt;&lt;strong&gt;Required.&lt;/strong&gt; The variable’s value.&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;drupal_root&lt;/code&gt;&lt;/td&gt;
      &lt;td&gt;String&lt;/td&gt;
      &lt;td&gt;&lt;strong&gt;Required.&lt;/strong&gt; Equivalent to &lt;code class=&quot;highlighter-rouge&quot;&gt;drush -r &amp;lt;path&amp;gt;&lt;/code&gt;, &lt;code class=&quot;highlighter-rouge&quot;&gt;--root=&amp;lt;path&amp;gt;&lt;/code&gt;.&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;drupal_uri&lt;/code&gt;&lt;/td&gt;
      &lt;td&gt;String&lt;/td&gt;
      &lt;td&gt;Equivalent to &lt;code class=&quot;highlighter-rouge&quot;&gt;drush -l &amp;lt;http://example.com:8888&amp;gt;&lt;/code&gt;, &lt;code class=&quot;highlighter-rouge&quot;&gt;--uri=&amp;lt;http://example.com:8888&amp;gt;&lt;/code&gt;.&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;shell_timeout&lt;/code&gt;&lt;/td&gt;
      &lt;td&gt;Integer&lt;/td&gt;
      &lt;td&gt;Set a timeout for the shell operation. Defaults to 900 (15 minutes).&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;shell_user&lt;/code&gt;&lt;/td&gt;
      &lt;td&gt;User&lt;/td&gt;
      &lt;td&gt;Run the Drush command as a specific user.&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;shell_group&lt;/code&gt;&lt;/td&gt;
      &lt;td&gt;Group&lt;/td&gt;
      &lt;td&gt;Run the Drush command as a member of a specific group.&lt;/td&gt;
    &lt;/tr&gt;
  &lt;/tbody&gt;
&lt;/table&gt;

&lt;h4 id=&quot;examples-1&quot;&gt;Examples&lt;/h4&gt;

&lt;p&gt;Enable CSS preprocessing:&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-ruby&quot; data-lang=&quot;ruby&quot;&gt;&lt;span class=&quot;n&quot;&gt;drush_variable&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;preprocess_css&quot;&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;value&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;drupal_root&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;/var/www/html&quot;&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;drupal_uri&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;http://www.example.com&quot;&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;shell_user&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;www-data&quot;&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;shell_group&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;www-data&quot;&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;h3 id=&quot;drushphpeval&quot;&gt;drush_php_eval&lt;/h3&gt;

&lt;p&gt;The &lt;strong&gt;drush_php_eval&lt;/strong&gt; lightweight resource executes a block of PHP code.&lt;/p&gt;

&lt;h4 id=&quot;actions-2&quot;&gt;Actions&lt;/h4&gt;

&lt;p&gt;This lightweight resource provider has the following actions:&lt;/p&gt;

&lt;table class=&quot;table&quot;&gt;
  &lt;thead&gt;
    &lt;tr&gt;
      &lt;th&gt;Action&lt;/th&gt;
      &lt;th&gt;Description&lt;/th&gt;
    &lt;/tr&gt;
  &lt;/thead&gt;
  &lt;tbody&gt;
    &lt;tr&gt;
      &lt;td&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;:execute&lt;/code&gt;&lt;/td&gt;
      &lt;td&gt;Default. Execute the PHP code.&lt;/td&gt;
    &lt;/tr&gt;
  &lt;/tbody&gt;
&lt;/table&gt;

&lt;h4 id=&quot;attributes-2&quot;&gt;Attributes&lt;/h4&gt;

&lt;p&gt;This lightweight resource provider has the following attributes:&lt;/p&gt;

&lt;table class=&quot;table&quot;&gt;
  &lt;thead&gt;
    &lt;tr&gt;
      &lt;th&gt;Attribute&lt;/th&gt;
      &lt;th&gt;Kind&lt;/th&gt;
      &lt;th&gt;Description&lt;/th&gt;
    &lt;/tr&gt;
  &lt;/thead&gt;
  &lt;tbody&gt;
    &lt;tr&gt;
      &lt;td&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;php&lt;/code&gt;&lt;/td&gt;
      &lt;td&gt;String&lt;/td&gt;
      &lt;td&gt;&lt;strong&gt;Required.&lt;/strong&gt; The PHP code. Examples: &lt;code class=&quot;highlighter-rouge&quot;&gt;variable_set('site_offline', 0)&lt;/code&gt;.&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;drupal_root&lt;/code&gt;&lt;/td&gt;
      &lt;td&gt;String&lt;/td&gt;
      &lt;td&gt;&lt;strong&gt;Required.&lt;/strong&gt; Equivalent to &lt;code class=&quot;highlighter-rouge&quot;&gt;drush -r &amp;lt;path&amp;gt;&lt;/code&gt;, &lt;code class=&quot;highlighter-rouge&quot;&gt;--root=&amp;lt;path&amp;gt;&lt;/code&gt;.&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;drupal_uri&lt;/code&gt;&lt;/td&gt;
      &lt;td&gt;String&lt;/td&gt;
      &lt;td&gt;Equivalent to &lt;code class=&quot;highlighter-rouge&quot;&gt;drush -l &amp;lt;http://example.com:8888&amp;gt;&lt;/code&gt;, &lt;code class=&quot;highlighter-rouge&quot;&gt;--uri=&amp;lt;http://example.com:8888&amp;gt;&lt;/code&gt;.&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;format&lt;/code&gt;&lt;/td&gt;
      &lt;td&gt;String&lt;/td&gt;
      &lt;td&gt;One of the following output formats: &lt;code class=&quot;highlighter-rouge&quot;&gt;var_export&lt;/code&gt;, &lt;code class=&quot;highlighter-rouge&quot;&gt;csv&lt;/code&gt;, &lt;code class=&quot;highlighter-rouge&quot;&gt;json&lt;/code&gt;, &lt;code class=&quot;highlighter-rouge&quot;&gt;list&lt;/code&gt;, &lt;code class=&quot;highlighter-rouge&quot;&gt;string&lt;/code&gt;, &lt;code class=&quot;highlighter-rouge&quot;&gt;table&lt;/code&gt;, &lt;code class=&quot;highlighter-rouge&quot;&gt;yaml&lt;/code&gt;. Defaults to &lt;code class=&quot;highlighter-rouge&quot;&gt;var_export&lt;/code&gt;.&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;shell_timeout&lt;/code&gt;&lt;/td&gt;
      &lt;td&gt;Integer&lt;/td&gt;
      &lt;td&gt;Set a timeout for the shell operation. Defaults to 900 (15 minutes).&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;shell_user&lt;/code&gt;&lt;/td&gt;
      &lt;td&gt;User&lt;/td&gt;
      &lt;td&gt;Run the Drush command as a specific user.&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;shell_group&lt;/code&gt;&lt;/td&gt;
      &lt;td&gt;Group&lt;/td&gt;
      &lt;td&gt;Run the Drush command as a member of a specific group.&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;block&lt;/code&gt;&lt;/td&gt;
      &lt;td&gt;Proc&lt;/td&gt;
      &lt;td&gt;Ruby block to act on the &lt;code class=&quot;highlighter-rouge&quot;&gt;stdout&lt;/code&gt; from the Drush command.&lt;/td&gt;
    &lt;/tr&gt;
  &lt;/tbody&gt;
&lt;/table&gt;

&lt;h4 id=&quot;examples-2&quot;&gt;Examples&lt;/h4&gt;

&lt;p&gt;Generate the &lt;code class=&quot;highlighter-rouge&quot;&gt;$drupal_hash_salt&lt;/code&gt; value, and save it into a file:&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-ruby&quot; data-lang=&quot;ruby&quot;&gt;&lt;span class=&quot;n&quot;&gt;drupal_hash_salt_path&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;/var/www/private/drupal_hash_salt.txt&quot;&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;drush_php_eval&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;generate_drupal_hash_salt&quot;&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;php&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;echo drupal_hash_base64(drupal_random_bytes(55))&quot;&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;drupal_root&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;/var/www/html&quot;&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;block&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;stdout&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;
    &lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;File&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;open&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;drupal_hash_salt_path&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;'w'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;f&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;|&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;f&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;write&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;stdout&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;not_if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;File&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;exists?&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;drupal_hash_salt_path&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;h3 id=&quot;drushmake&quot;&gt;drush_make&lt;/h3&gt;

&lt;p&gt;The &lt;strong&gt;drush_make&lt;/strong&gt; lightweight resource turns a given makefile into a Drupal codebase.&lt;/p&gt;

&lt;h4 id=&quot;actions-3&quot;&gt;Actions&lt;/h4&gt;

&lt;p&gt;This lightweight resource provider has the following actions:&lt;/p&gt;

&lt;table class=&quot;table&quot;&gt;
  &lt;thead&gt;
    &lt;tr&gt;
      &lt;th&gt;Action&lt;/th&gt;
      &lt;th&gt;Description&lt;/th&gt;
    &lt;/tr&gt;
  &lt;/thead&gt;
  &lt;tbody&gt;
    &lt;tr&gt;
      &lt;td&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;:install&lt;/code&gt;&lt;/td&gt;
      &lt;td&gt;Default. Build the makefile.&lt;/td&gt;
    &lt;/tr&gt;
  &lt;/tbody&gt;
&lt;/table&gt;

&lt;h4 id=&quot;attributes-3&quot;&gt;Attributes&lt;/h4&gt;

&lt;p&gt;This lightweight resource provider has the following attributes:&lt;/p&gt;

&lt;table class=&quot;table&quot;&gt;
  &lt;thead&gt;
    &lt;tr&gt;
      &lt;th&gt;Attribute&lt;/th&gt;
      &lt;th&gt;Kind&lt;/th&gt;
      &lt;th&gt;Description&lt;/th&gt;
    &lt;/tr&gt;
  &lt;/thead&gt;
  &lt;tbody&gt;
    &lt;tr&gt;
      &lt;td&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;build_path&lt;/code&gt;&lt;/td&gt;
      &lt;td&gt;String&lt;/td&gt;
      &lt;td&gt;&lt;strong&gt;Required.&lt;/strong&gt; The path at which to build the makefile.&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;makefile&lt;/code&gt;&lt;/td&gt;
      &lt;td&gt;String&lt;/td&gt;
      &lt;td&gt;&lt;strong&gt;Required.&lt;/strong&gt; Filename or URI of the makefile to use for this build.&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;shell_timeout&lt;/code&gt;&lt;/td&gt;
      &lt;td&gt;Integer&lt;/td&gt;
      &lt;td&gt;Set a timeout for the shell operation. Defaults to 900 (15 minutes).&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;shell_user&lt;/code&gt;&lt;/td&gt;
      &lt;td&gt;User&lt;/td&gt;
      &lt;td&gt;Run the Drush command as a specific user.&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;shell_group&lt;/code&gt;&lt;/td&gt;
      &lt;td&gt;Group&lt;/td&gt;
      &lt;td&gt;Run the Drush command as a member of a specific group.&lt;/td&gt;
    &lt;/tr&gt;
  &lt;/tbody&gt;
&lt;/table&gt;

&lt;h4 id=&quot;examples-3&quot;&gt;Examples&lt;/h4&gt;

&lt;p&gt;Build the &lt;code class=&quot;highlighter-rouge&quot;&gt;example.make&lt;/code&gt; makefile:&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-ruby&quot; data-lang=&quot;ruby&quot;&gt;&lt;span class=&quot;n&quot;&gt;drush_make&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;/var/www/html&quot;&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;makefile&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;http://example.com/examples/example.make&quot;&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;shell_user&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;www-data&quot;&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;shell_group&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;www-data&quot;&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;h3 id=&quot;drushsiteinstall&quot;&gt;drush_site_install&lt;/h3&gt;

&lt;p&gt;The &lt;strong&gt;drush_site_install&lt;/strong&gt; lightweight resource installs Drupal along with modules/themes/configuration using the specified install profile.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; this provider assumes that &lt;code class=&quot;highlighter-rouge&quot;&gt;settings.php&lt;/code&gt; already has the database credentials provided for the given &lt;code class=&quot;highlighter-rouge&quot;&gt;drupal_uri&lt;/code&gt;. You can use a &lt;code class=&quot;highlighter-rouge&quot;&gt;template&lt;/code&gt; resource to define &lt;code class=&quot;highlighter-rouge&quot;&gt;settings.php&lt;/code&gt;.&lt;/p&gt;

&lt;h4 id=&quot;actions-4&quot;&gt;Actions&lt;/h4&gt;

&lt;p&gt;This lightweight resource provider has the following actions:&lt;/p&gt;

&lt;table class=&quot;table&quot;&gt;
  &lt;thead&gt;
    &lt;tr&gt;
      &lt;th&gt;Action&lt;/th&gt;
      &lt;th&gt;Description&lt;/th&gt;
    &lt;/tr&gt;
  &lt;/thead&gt;
  &lt;tbody&gt;
    &lt;tr&gt;
      &lt;td&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;:install&lt;/code&gt;&lt;/td&gt;
      &lt;td&gt;Default. Install Drupal from the specified install profile.&lt;/td&gt;
    &lt;/tr&gt;
  &lt;/tbody&gt;
&lt;/table&gt;

&lt;h4 id=&quot;attributes-4&quot;&gt;Attributes&lt;/h4&gt;

&lt;p&gt;This lightweight resource provider has the following attributes:&lt;/p&gt;

&lt;table class=&quot;table&quot;&gt;
  &lt;thead&gt;
    &lt;tr&gt;
      &lt;th&gt;Attribute&lt;/th&gt;
      &lt;th&gt;Kind&lt;/th&gt;
      &lt;th&gt;Description&lt;/th&gt;
    &lt;/tr&gt;
  &lt;/thead&gt;
  &lt;tbody&gt;
    &lt;tr&gt;
      &lt;td&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;profile&lt;/code&gt;&lt;/td&gt;
      &lt;td&gt;String&lt;/td&gt;
      &lt;td&gt;&lt;strong&gt;Required.&lt;/strong&gt; The install profile to use.&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;force&lt;/code&gt;&lt;/td&gt;
      &lt;td&gt;Boolean&lt;/td&gt;
      &lt;td&gt;Force the installation. Defaults to &lt;code class=&quot;highlighter-rouge&quot;&gt;false&lt;/code&gt;.&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;site_name&lt;/code&gt;&lt;/td&gt;
      &lt;td&gt;String&lt;/td&gt;
      &lt;td&gt;Optionally provide the site name.&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;drupal_root&lt;/code&gt;&lt;/td&gt;
      &lt;td&gt;String&lt;/td&gt;
      &lt;td&gt;&lt;strong&gt;Required.&lt;/strong&gt; Equivalent to &lt;code class=&quot;highlighter-rouge&quot;&gt;drush -r &amp;lt;path&amp;gt;&lt;/code&gt;, &lt;code class=&quot;highlighter-rouge&quot;&gt;--root=&amp;lt;path&amp;gt;&lt;/code&gt;.&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;drupal_uri&lt;/code&gt;&lt;/td&gt;
      &lt;td&gt;String&lt;/td&gt;
      &lt;td&gt;Equivalent to &lt;code class=&quot;highlighter-rouge&quot;&gt;drush -l &amp;lt;http://example.com:8888&amp;gt;&lt;/code&gt;, &lt;code class=&quot;highlighter-rouge&quot;&gt;--uri=&amp;lt;http://example.com:8888&amp;gt;&lt;/code&gt;.&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;shell_timeout&lt;/code&gt;&lt;/td&gt;
      &lt;td&gt;Integer&lt;/td&gt;
      &lt;td&gt;Set a timeout for the shell operation. Defaults to 900 (15 minutes).&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;shell_user&lt;/code&gt;&lt;/td&gt;
      &lt;td&gt;User&lt;/td&gt;
      &lt;td&gt;Run the Drush command as a specific user.&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;shell_group&lt;/code&gt;&lt;/td&gt;
      &lt;td&gt;Group&lt;/td&gt;
      &lt;td&gt;Run the Drush command as a member of a specific group.&lt;/td&gt;
    &lt;/tr&gt;
  &lt;/tbody&gt;
&lt;/table&gt;

&lt;h4 id=&quot;examples-4&quot;&gt;Examples&lt;/h4&gt;

&lt;p&gt;Install Drupal using the standard profile:&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-ruby&quot; data-lang=&quot;ruby&quot;&gt;&lt;span class=&quot;n&quot;&gt;drush_site_install&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;standard&quot;&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;drupal_root&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;/var/www/html&quot;&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;drupal_uri&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;http://www.example.com&quot;&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;site_name&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;Example&quot;&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;h3 id=&quot;helper-library&quot;&gt;Helper library&lt;/h3&gt;

&lt;p&gt;There is a helper library to assist with some of the idempotence.&lt;/p&gt;

&lt;h4 id=&quot;drupalpresentpath&quot;&gt;drupal_present?(path)&lt;/h4&gt;

&lt;p&gt;This verifies that Drupal core is present at &lt;code class=&quot;highlighter-rouge&quot;&gt;path&lt;/code&gt;, but isn’t necessarily bootstrapped via &lt;code class=&quot;highlighter-rouge&quot;&gt;drush status&lt;/code&gt;.&lt;/p&gt;

&lt;h4 id=&quot;drupalinstalledpath-uri--httpdefault&quot;&gt;drupal_installed?(path, uri = ‘http://default’)&lt;/h4&gt;

&lt;p&gt;This verifies that Drupal successfully bootstraps at &lt;code class=&quot;highlighter-rouge&quot;&gt;path&lt;/code&gt; and &lt;code class=&quot;highlighter-rouge&quot;&gt;uri&lt;/code&gt; (via &lt;code class=&quot;highlighter-rouge&quot;&gt;drush status&lt;/code&gt;).&lt;/p&gt;

&lt;h4 id=&quot;drushvgetjsonpath-name-uri--httpdefault-exactmatch--true&quot;&gt;drush_vget_json(path, name, uri = ‘http://default’, exact_match = true)&lt;/h4&gt;

&lt;p&gt;This returns a JSON Object with the results of &lt;code class=&quot;highlighter-rouge&quot;&gt;drush variable-get&lt;/code&gt;.&lt;/p&gt;

&lt;h4 id=&quot;drushvgetmatchpath-name-value-uri--httpdefault&quot;&gt;drush_vget_match?(path, name, value, uri = ‘http://default’)&lt;/h4&gt;

&lt;p&gt;This verifies that a Drupal variable &lt;code class=&quot;highlighter-rouge&quot;&gt;name&lt;/code&gt; matches the given &lt;code class=&quot;highlighter-rouge&quot;&gt;value&lt;/code&gt;.&lt;/p&gt;</content><author><name>Ben Clark</name></author><category term="drupal" /><category term="chef" /><summary type="html">I attended Opscode Chef training a couple weeks ago and got super-psyched about it. I’m working on a project for Jackson River, and utilizing Chef is a big component. While writing the cookbook for this project, I found that I needed more access to Drush.</summary></entry><entry><title type="html">10 performance tweaks to make before applying Varnish on your Drupal site</title><link href="/articles/drupal-performance-tweaks-before-applying-varnish.html" rel="alternate" type="text/html" title="10 performance tweaks to make before applying Varnish on your Drupal site" /><published>2013-05-30T00:00:00-04:00</published><updated>2013-05-30T00:00:00-04:00</updated><id>/articles/drupal-performance-tweaks-before-applying-varnish</id><content type="html" xml:base="/articles/drupal-performance-tweaks-before-applying-varnish.html">&lt;p&gt;Step 11: Enable Varnish. But steps 1 through 10 matter more. Varnish isn’t a magical solution to all your performance woes. In truth, it’s one of the more aptly-named software packages: you should only apply it only after you’ve finished sanding down the rough edges of your site’s performance. If you apply it too soon, you won’t have the smooth surface you were hoping for, and at best you’ll be covering over your site’s imperfections.&lt;/p&gt;

&lt;p&gt;Here are some topics I run through before enabling Varnish on a Drupal site. Your mileage may vary, and your methods may vary as well. This list is designed for you to do a deep dive into finding out why your site is “slow,” and I like to perform some version of this list for every site launch I participate in.&lt;/p&gt;

&lt;h3 id=&quot;have-you-tried-the-built-in-page-cache&quot;&gt;1) Have you tried the built-in page cache?&lt;/h3&gt;

&lt;ul&gt;
  &lt;li&gt;Have you tried turning on the built-in page cache?&lt;/li&gt;
  &lt;li&gt;Do the pages cache as expected, or are you still getting uncached content delivered?&lt;/li&gt;
  &lt;li&gt;Are there any portions of your site that no longer work as expected with the built-in page cache enabled?&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If turning on the built-in page cache causes headaches, then you likely have some architectural decisions to make.&lt;/p&gt;

&lt;p&gt;For example: Drupal core comments are a fun way to “engage” with your visitors, but they’re going to be a real pain to work around if you want effective Varnish caching. But more importantly, do they really get any use on your site, aside from spammers? Could you get the same level of engagement from a third-party commenting system like Disqus, something that exists only on the client side, and thus doesn’t require anything from Drupal?&lt;/p&gt;

&lt;h3 id=&quot;have-you-done-any-php-profiling&quot;&gt;2) Have you done any PHP profiling?&lt;/h3&gt;

&lt;ul&gt;
  &lt;li&gt;Have you done any PHP profiling (using xhprof, others)?&lt;/li&gt;
  &lt;li&gt;Do you know which of your pages take the most time to load, and do you know why they take as long as they do?&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Anytime you introduce custom code, you’re introducing something that has not been fully tested at scale.&lt;/p&gt;

&lt;p&gt;Be mindful of trying to optimize &lt;em&gt;too&lt;/em&gt; much — saving a few dozen milliseconds off of a page request will be nearly imperceptible and barely worth the effort. On the flip side, if you can shave half a second or more off, that’s worth even a significant refactor of your code.&lt;/p&gt;

&lt;h3 id=&quot;are-there-any-views-that-take-a-long-time-to-load&quot;&gt;3) Are there any views that take a long time to load?&lt;/h3&gt;

&lt;ul&gt;
  &lt;li&gt;Are there any views that take a long time to load?&lt;/li&gt;
  &lt;li&gt;Are they cacheable using a Views cache plugin, or would caching the view break it somehow?&lt;/li&gt;
  &lt;li&gt;And if so, why?&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Writing your own Views cache plugin is easy, so if the built-in time-based cache is insufficient for your needs, feel free to implement your own. For example, if you have view output that is identical for logged-in and anonymous visitors, then your custom Views cache plugin can set a results and output key that does &lt;em&gt;not&lt;/em&gt; include the logged-in user’s roles as part of the hash.&lt;/p&gt;

&lt;h3 id=&quot;are-there-any-panels-panes-that-take-a-while-to-load&quot;&gt;4) Are there any Panels panes that take a while to load?&lt;/h3&gt;

&lt;ul&gt;
  &lt;li&gt;Similar to the previous Views question, are there any Panels panes that take a while to load?&lt;/li&gt;
  &lt;li&gt;Are they cacheable? (I’m going to strongly urge you to answer yes to that question, even if it requires some extra thought.)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The built-in simple cache may be too simple for your needs, but there are some excellent Panels caching modules already available, and a Panels cache plugin is relatively simple to implement if you need a custom solution.&lt;/p&gt;

&lt;h3 id=&quot;how-about-ssl&quot;&gt;5) How about SSL?&lt;/h3&gt;

&lt;ul&gt;
  &lt;li&gt;Are there any SSL pages?&lt;/li&gt;
  &lt;li&gt;Are you using a module like Securepages to redirect certain paths to SSL?&lt;/li&gt;
  &lt;li&gt;What percentage of your traffic will be SSL?&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Bottom line, unless you’re doing SSL termination, traffic coming in over port 443 is never going to be cached by Varnish. A page that renders slowly before you enable Varnish is going to be just as slow for your SSL traffic. Instead of allowing any page to be encrypted, use a module like Securepages to redirect your traffic back to port 80 (and back to Varnish) unless they’re on a specific set of pages.&lt;/p&gt;

&lt;h3 id=&quot;can-users-log-in&quot;&gt;6) Can users log in?&lt;/h3&gt;

&lt;ul&gt;
  &lt;li&gt;Do your users have the ability to log in, to create profiles, etc.?&lt;/li&gt;
  &lt;li&gt;Is there any value added to the UX if they’re logged in?&lt;/li&gt;
  &lt;li&gt;Is there any content that changes based on whether your visitors are authenticated or not?&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Users are the enemy of a performant Drupal site. Having a user session automatically bypasses Varnish, and thus as long as your visitor is logged in — regardless of whether they’re viewing a members-only forum or the About Us page — they are not going to be served by Varnish. If you don’t absolutely need users, consider blocking them altogether.&lt;/p&gt;

&lt;p&gt;If, however, having logged-in users is part of your build plan, take a minute to review alternative caching options. Views and Panels can cache content differently for logged-in and anonymous traffic. And if you have a custom menu callback, consider writing your own &lt;code class=&quot;highlighter-rouge&quot;&gt;cache_get()&lt;/code&gt; and &lt;code class=&quot;highlighter-rouge&quot;&gt;cache_set()&lt;/code&gt; logic to spare any expensive &lt;code class=&quot;highlighter-rouge&quot;&gt;node_load()&lt;/code&gt; or database calls.&lt;/p&gt;

&lt;h3 id=&quot;are-you-running-pressflow&quot;&gt;7) Are you running Pressflow?&lt;/h3&gt;

&lt;ul&gt;
  &lt;li&gt;Are you running Drupal 6?&lt;/li&gt;
  &lt;li&gt;Are you running the Pressflow fork of Drupal 6?&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;(Drupal 7 developers can skip this section altogether.)&lt;/p&gt;

&lt;p&gt;Bottom line, if you’re looking to have Varnish cache your Drupal 6 site, using Pressflow is an absolute requirement. It simply will not work without it. Pressflow is a drop-in replacement for Drupal 6 core, and 99% of sites will function exactly the same — if not better — with Pressflow. Still, it doesn’t hurt to run some of your test coverage again, or, in lieu of test coverage, to test some of your custom functionality by hand.&lt;/p&gt;

&lt;p&gt;Pressflow also includes some fun extras like path alias caching, which caches frequent path lookup database requests, as well as some core tweaks that help Drupal 6 in enterprise environments. A simple blog site wouldn’t see those benefits, but a large corporate site could.&lt;/p&gt;

&lt;h3 id=&quot;are-you-making-any-api-calls&quot;&gt;8) Are you making any API calls?&lt;/h3&gt;

&lt;ul&gt;
  &lt;li&gt;Are you making any third-party API calls, like to a payment gateway, or to Twitter?&lt;/li&gt;
  &lt;li&gt;Are you running modules that periodically call out to get authentication tokens, like Mollom?&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If you are, you’re completely at the mercy of the third-party’s API performance and how frequently you request data from it.&lt;/p&gt;

&lt;p&gt;In load testing one of our client sites recently, I was getting terrible performance on a site that was sitting on some heavy-duty hardware, and it just didn’t seem right. I broke out the PHP profiler (xhprof), and discovered that the culprit was Mollom. A little known quirk of the Mollom module for Drupal is that when Mollom is in testing/development mode, it issues an API request on every page request as long as there’s at least one form on that page. That form doesn’t even have to be Mollom-enabled, it could just be the search form in the header.&lt;/p&gt;

&lt;p&gt;In this specific case, I offered a patch to the module maintainer, and disabled testing/development mode in the module settings. But it was a lesson learned: any API call at all, no matter how benign, could have a measurable impact on your site’s performance, especially at scale.&lt;/p&gt;

&lt;h3 id=&quot;now-we-talk-about-optimizing-your-stack&quot;&gt;9) Now we talk about optimizing your stack&lt;/h3&gt;

&lt;p&gt;Now it’s time to review hardware and server configuration. Note how I haven’t mentioned anything about the server environment until now.&lt;/p&gt;

&lt;p&gt;There’s plenty of server jockeys out there who will insist that the first rule of getting Drupal to be performant is installing PHP-FPM, nginx, Memcache, and Varnish. They might even recommend Project Mercury, which is a (wonderful) install script for optimizing your entire stack.&lt;/p&gt;

&lt;p&gt;As much as I love those tools, no matter how much stack optimization you do, you can still have a page on your site that issues 11,000 database calls that saturate your database server’s I/O, causing the page render to take over 40 seconds to complete, which ties up a PHP resource on your application server while it waits. Swapping out mod_php for PHP-FPM and tweaking &lt;code class=&quot;highlighter-rouge&quot;&gt;my.cnf&lt;/code&gt; might take it from 40 seconds down to 35 seconds, but we’re still talking about a &lt;em&gt;massive&lt;/em&gt; liability that, truly, has nothing to do with the stack and everything to do with how your site is built.&lt;/p&gt;

&lt;p&gt;Remember that Varnish caches aren’t forever, and there will come a time when one of your visitors will hit that page completely uncached.&lt;/p&gt;

&lt;p&gt;Am I suggesting that running Memcache isn’t an awesome way to optimize your cache bins? Absolutely not — and by all means, install it and use it, especially if you’re making use of Views and Panels caches, block caches, custom object caches, etc., or if your database layer isn’t on stellar hardware (or worse, if it’s on the same hardware). And the other optimizations are just as valuable. I’m simply arguing in favor of reviewing your Drupal build configuration first. Bottom line, check your custom code and your design decisions before you blame time-tested code like Apache and mod_php.&lt;/p&gt;

&lt;h3 id=&quot;and-now-for-something-completely-out-of-left-field&quot;&gt;10) And now for something completely out of left field…&lt;/h3&gt;

&lt;p&gt;I’m going to suggest something completely out of the box, and I apologize if it ruffles the feathers of any die-hard Drupal developers out there, but: is Drupal the best solution for your site?&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Could your site be rendered into static HTML files and still perform exactly the same?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Before you dismiss this question on spec, consider that you’re seeking to do &lt;em&gt;exactly that&lt;/em&gt;, just with Varnish. All Varnish is going to do is render your Drupal pages into static files that it stores either on disk or in memory, and those static files are what get served to your visitors. Yes, with Varnish you do have the option of passing through your dynamic requests — AJAX calls, form submissions, etc. — to Drupal, but a 100%-effective Varnished site is going to be equivalent to a series of static HTML (or XML, JSON, etc.) files.&lt;/p&gt;

&lt;p&gt;So, if your site is 99% static content, take a minute to ask yourself if the overhead of running a full-fledged content management system is worth the performance hit (and the administrative hassle). Consider also that rendering it into static files means you can upload them directly to a CDN, and you can skip the overhead of running your own servers. For some clients, the server infrastructure can be a significant budget line item, and it could be largely unnecessary.&lt;/p&gt;

&lt;p&gt;If you’re seeking a way to automate the rendering of content into static files, look into projects like Jekyll and its derivatives. They’re not Drupal-based — they’re not even PHP-based — but they accept plain text files, Markdown, or HTML (really, any format you require), and plug the content into templates, and can support any metadata you need.&lt;/p&gt;

&lt;h3 id=&quot;if-youve-made-it-this-far-enable-varnish&quot;&gt;11) If you’ve made it this far, enable Varnish&lt;/h3&gt;

&lt;p&gt;If you’ve made it this far, feel free to enable Varnish — and enjoy a wildly-performant Drupal site. After all, you’ve taken the time to optimize top to bottom, and your site is moderately-performant when completely uncached. Adding Varnish on top of that smooth surface is going to mean so much more now.&lt;/p&gt;</content><author><name>Ben Clark</name></author><category term="drupal" /><summary type="html">Step 11: Enable Varnish. But steps 1 through 10 matter more. Varnish isn’t a magical solution to all your performance woes. In truth, it’s one of the more aptly-named software packages: you should only apply it only after you’ve finished sanding down the rough edges of your site’s performance. If you apply it too soon, you won’t have the smooth surface you were hoping for, and at best you’ll be covering over your site’s imperfections.</summary></entry><entry><title type="html">Some of the better band names from three years of My Band Needs a Name</title><link href="/articles/my-band-needs-a-name.html" rel="alternate" type="text/html" title="Some of the better band names from three years of My Band Needs a Name" /><published>2013-05-24T00:00:00-04:00</published><updated>2013-05-24T00:00:00-04:00</updated><id>/articles/my-band-needs-a-name</id><content type="html" xml:base="/articles/my-band-needs-a-name.html">&lt;p&gt;&lt;img class=&quot;post-image pull-right&quot; src=&quot;http://benclark.com/files/mybandneedsaname-logo.png&quot; alt=&quot;My Band Needs a Name logo&quot; /&gt;&lt;/p&gt;

&lt;p&gt;For three years, I ran a little-known site called My Band Needs a Name. When I say “little-known,” I mean that maybe five people knew it existed.&lt;/p&gt;

&lt;p&gt;It was meant to be a repository of user-contributed band names, so that if you started a new band and needed a clever name, you could find one. And if you thought of a clever name on your own, you could record it.&lt;/p&gt;

&lt;p&gt;It was never more than a proof-of-concept, unfortunately, and last month, I was faced with a decision to renew the domain or not. Do I spend the time to make it right — by bringing it up-to-date with a mobile-friendly design, adding much-needed social sharing widgets, etc. — or do I just let it expire and move on to other projects?&lt;/p&gt;

&lt;p&gt;I chose the latter, but as a tribute of sorts, here are some of the better contributed band names with their top-voted descriptions.&lt;/p&gt;

&lt;p&gt;–&lt;/p&gt;

&lt;h4 id=&quot;mr-roboto-and-the-sushi-rolls&quot;&gt;MR. ROBOTO AND THE SUSHI ROLLS&lt;/h4&gt;

&lt;p&gt;J-pop band, heavy on the synthesizers, rapid-fire double kick drums, and near-continuous sweeps on the Ibanez.&lt;/p&gt;

&lt;h4 id=&quot;xor&quot;&gt;XOR&lt;/h4&gt;

&lt;p&gt;This band is either rockin’ or full of suck, but never both.&lt;/p&gt;

&lt;h4 id=&quot;quadratonic&quot;&gt;QUADRATONIC&lt;/h4&gt;

&lt;p&gt;One better than a tri-toned band. Duh.&lt;/p&gt;

&lt;h4 id=&quot;gary-sineses-pet-rock&quot;&gt;GARY SINESE’S PET ROCK&lt;/h4&gt;

&lt;p&gt;Academy Award winning themed bluegrass. Think Lieutenant Dan with a banjo.&lt;/p&gt;

&lt;h4 id=&quot;mc-plus-plus-and-the-al-gore-rhythms&quot;&gt;MC PLUS-PLUS AND THE AL GORE RHYTHMS&lt;/h4&gt;

&lt;p&gt;Old school hip-hop/polisci-nerdcore comprised of grad students.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Honorable mention:&lt;/em&gt; Environmentally-friendly “green”-core hip-hop group from Alaska.&lt;/p&gt;

&lt;h4 id=&quot;a-singer-and-some-session-musicians&quot;&gt;A SINGER AND SOME SESSION MUSICIANS&lt;/h4&gt;

&lt;p&gt;Maybe some sort of tween pop-rock “band” – produced by Disney, for idiots.&lt;/p&gt;

&lt;h4 id=&quot;pneumonoultramicroscopicsilicovolcanoconiosis&quot;&gt;PNEUMONOULTRAMICROSCOPICSILICOVOLCANOCONIOSIS&lt;/h4&gt;

&lt;p&gt;The longest word in the English language is now the longest band in the English language!&lt;/p&gt;

&lt;h4 id=&quot;the-jerry-dalton-all-star-orchestra&quot;&gt;THE JERRY DALTON ALL-STAR ORCHESTRA&lt;/h4&gt;

&lt;p&gt;Jerry Dalton, before becoming the third-best used Saab salesman in Memphis, led his All-Star Orchestra in the style of big band and ska. They played mostly weddings.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Honorable mention:&lt;/em&gt; Big band electronica/death-metal with a charismatic bandleader in Jerry Dalton (not his real name, of course). Think baby blue tuxedos with blood stains. And a horn section.&lt;/p&gt;

&lt;h3 id=&quot;and-my-all-time-favorite&quot;&gt;And my all time favorite…&lt;/h3&gt;

&lt;h4 id=&quot;dr-professor-and-the-scan-trons&quot;&gt;DR. PROFESSOR AND THE SCAN-TRONS&lt;/h4&gt;

&lt;p&gt;Surf rock with a ska riff or two, with the occasional foray into kid-friendly alt folk rock.&lt;/p&gt;</content><author><name>Ben Clark</name></author><category term="projects" /><summary type="html"></summary></entry><entry><title type="html">I miss my iPhone (but I can’t switch back, yet)</title><link href="/articles/i-miss-my-iphone.html" rel="alternate" type="text/html" title="I miss my iPhone (but I can't switch back, yet)" /><published>2013-05-19T00:00:00-04:00</published><updated>2013-05-19T00:00:00-04:00</updated><id>/articles/i-miss-my-iphone</id><content type="html" xml:base="/articles/i-miss-my-iphone.html">&lt;p&gt;I switched to a Nexus 4 back in February, partly out of tech curiosity and partly out of frustration with stagnant iOS innovation. While I still believe that iOS is stagnant and behind the times, with WWDC a few short weeks away, I have renewed hope that Apple can turn it around.&lt;/p&gt;

&lt;p&gt;I’ll be honest: I miss my iPhone. I miss the little things that “just worked” and I miss the quality of the apps. I don’t like stressing about app permissions, and I don’t find myself wanting to customize it as much as Android would allow me.&lt;/p&gt;

&lt;p&gt;But at the same time, I know I can’t switch back yet, not until iOS (and the iPhone itself) catches up in some of the areas where Android has really taken a strong lead. I still love the way accounts are handled, and how services within the OS (like the web browser, text messaging, and email) can be replaced or augmented — at the OS level — by third-party apps. Oh, and the iPhone screen looks comically tiny now compared to my Nexus 4.&lt;/p&gt;

&lt;h2 id=&quot;i-miss-my-iphone&quot;&gt;I miss my iPhone&lt;/h2&gt;

&lt;h3 id=&quot;it-just-worked&quot;&gt;It just worked&lt;/h3&gt;

&lt;p&gt;My iPhone “just worked.” That isn’t to say that my Nexus 4 has been exceptionally buggy. All software is buggy, and hardware isn’t perfect either. It’s more of a perception thing, I guess, a general vibe that I get from using it.&lt;/p&gt;

&lt;p&gt;Actually, I blame the apps far more than I blame the Android OS or the Nexus 4 hardware. The quality of the apps are just not very consistent, and often feel like afterthoughts to their iOS counterparts. My first experiences with most of these apps were on iOS, where the UX is tightly controlled through APIs and HIGs that established early on exactly what an iOS app should look and feel like. So, when I started using the apps on Android, they just felt inferior in their inconsistency — inconsistencies not only with their iOS counterparts, but within the Android OS, and amongst other apps.&lt;/p&gt;

&lt;p&gt;I’m not going to offer up a laundry list of apps that are terrible, but instead I’ll name some that are an exception to what I just said, where their iOS and Android versions are, for the most part, comparable in UX and functionality:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;strong&gt;Facebook&lt;/strong&gt; — this takes the prize for being just the worst on both platforms. I absolutely hate using it on any mobile device.&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Twitter&lt;/strong&gt; — actually quite decent on both platforms, and bonus points for having a superior web experience to match the apps.&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Harvest&lt;/strong&gt; — this is my time tracking software that I use for work. It’s a solid, reliable app on iOS, and the Android version is a clean port.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3 id=&quot;android-apps-can-do-too-much&quot;&gt;Android apps can do too much&lt;/h3&gt;

&lt;p&gt;Something that stresses me out probably shouldn’t stress me out as much as it does: app permissions. Anytime I download or update an app, I’m prompted with a reminder that this app can do &lt;em&gt;way too much&lt;/em&gt; on my phone. Oh, I can’t &lt;em&gt;do&lt;/em&gt; anything about it, it’s just a notification.&lt;/p&gt;

&lt;p&gt;Say what you will about iOS, but it has a pretty tight API. There have been a few notable exceptions — at one point, the LinkedIn iOS app did something quasi-malicious with your contacts — but they’ve invariably been turned around on the app authors as violations of the API and TOS. It doesn’t usually end well: the app gets pulled from the App Store, or the company behind the app suffers some bad PR and fixes the problem on their own.&lt;/p&gt;

&lt;p&gt;Conversely, in the Android space, it does seem as though apps are given free reign to seemingly low-level endpoints in the OS. I can download an app that shows me intimate detail about the other apps that are running: memory usage, shared library utilization, etc. Without much hands-on knowledge of the iOS API, I’m pretty confident that level of access is either a no-go in iOS or far more limited in scope.&lt;/p&gt;

&lt;p&gt;There was a story that went around a few weeks ago that upgrading to the newest version of the Facebook app added some new code that allows the app to update itself in the future, skipping the Google Play Store completely for managing updates. As a software developer, I totally get why Facebook would add that code. As an end user, it scares the crap out of me. I don’t trust Facebook’s intentions 6 months from now when they decide that &lt;em&gt;everyone&lt;/em&gt; should have Facebook Home.&lt;/p&gt;

&lt;p&gt;I like that I can trust iOS to prevent an app from doing anything predatory or out-of-scope. And when Apple has allowed its apps more access to system services like Location and Contacts, it has done so in a way that I can turn completely off. (Sorry, LinkedIn, I don’t trust you with access to my contacts. Sorry, Facebook, you don’t need to know where I am right now.)&lt;/p&gt;

&lt;h3 id=&quot;i-dont-customize-as-much-as-i-thought-i-would&quot;&gt;I don’t customize as much as I thought I would&lt;/h3&gt;

&lt;p&gt;I’ll admit flexibility is a strength of Android. Conceptually, I like that I can change almost anything about the OS. I can download an app to fix something I don’t like about the OS, and the OS doesn’t mind. And if I really hate something about the OS, there’s always the option to root and go completely nuts.&lt;/p&gt;

&lt;p&gt;But three months into owning my Nexus 4, do you want to know the truth? I don’t customize: I set it and forget it.&lt;/p&gt;

&lt;p&gt;It’s been pointed out to me that every complaint I have for the OS can be rectified with either an app or a new ROM. I wouldn’t argue against that, but I think the fact that I haven’t done those things yet — and that I’m still running the stock version of the OS that came with my phone, with no app-level modifications — says a lot about how I want to use my phone and how I view it as a utility device and not as a general-purpose computing device that I can hack away at until my heart’s content.&lt;/p&gt;

&lt;h2 id=&quot;but-i-cant-switch-back-yet&quot;&gt;But I can’t switch back… yet&lt;/h2&gt;

&lt;h3 id=&quot;i-still-love-the-way-accounts-are-handled&quot;&gt;I still love the way accounts are handled&lt;/h3&gt;

&lt;p&gt;iOS is slowly getting there with the integration of Twitter and Facebook as additional “accounts,” but they’re one-off exceptions to the general rule that an app stands alone on iOS. That bothers me a little, because that assumes Apple’s pre-installed apps are always superior. Oh, except in the areas of social sharing (Twitter) and social networking (Facebook). Oh, and once upon a time, in mapping (Google Maps, pre-iOS 6) and streaming videos (pre-installed YouTube).&lt;/p&gt;

&lt;p&gt;Before I switched, I was using Google’s Gmail app on iOS for all my mail, and it was working beautifully except for when I wanted to email myself a link from Safari. I would have to copy the URL to the clipboard and paste it into an email myself. There wasn’t a way for the Gmail app to announce itself as a fully-functioning alternative to the built-in Mail app.&lt;/p&gt;

&lt;p&gt;Similarly, the Instapaper iOS app requires extra steps to save a link to read later. I can either use the bookmarklet in Safari — which pings instapaper.com with my URL, which is then later downloaded into the Instapaper app — or I can copy/paste the URL from Safari into Instapaper. On Android, this is a lot simpler: there’s a sharing menu, under which Instapaper is an option. The app context switches briefly, and then returns to Chrome.&lt;/p&gt;

&lt;h3 id=&quot;the-nexus-4-screen&quot;&gt;The Nexus 4 screen&lt;/h3&gt;

&lt;p&gt;I never thought I’d say this, but I’ve really grown to like the form factor of the Nexus 4 screen. It’s still a bit wide for my comfort, but it’s larger in the right proportions compared to my iPhone 4.&lt;/p&gt;

&lt;p&gt;Also, the size of the screen isn’t preventing a switch back to an iPhone in general. It’s not so much of an issue that the iPhone screen remains smaller &lt;em&gt;if&lt;/em&gt; the available space can be used more effectively. The trend of skeuomorphic design is over and done, and a switch to cleaner and leaner design will free up a lot of wasted space in apps like Contacts and Calendar. And with the advent of better email clients like Gmail or Mailbox, I hope that we can at least see an UX improvement in the stock Mail app, if not to also gain the ability to completely replace it.&lt;/p&gt;

&lt;h2 id=&quot;i-have-hope-that-ios-can-bounce-back&quot;&gt;I have hope that iOS can bounce back&lt;/h2&gt;

&lt;p&gt;I have hope I’ll be an iPhone owner yet again. I still have my iPhone 4 sitting in my desk drawer, waiting for the moment that I can download the new version and start using it full-time again. Still… the realist in me says that any improvements in iOS and in the iPhone are going to be incremental. I enjoy my Nexus 4 and the freedoms it affords me, but… I do miss my iPhone, and I want to come back into the fold.&lt;/p&gt;</content><author><name>Ben Clark</name></author><category term="android" /><category term="ios" /><category term="iphone" /><summary type="html">I switched to a Nexus 4 back in February, partly out of tech curiosity and partly out of frustration with stagnant iOS innovation. While I still believe that iOS is stagnant and behind the times, with WWDC a few short weeks away, I have renewed hope that Apple can turn it around.</summary></entry><entry><title type="html">I bought a Google Nexus 4 to replace my iPhone 4. There, I said it.</title><link href="/articles/replaced-iphone-4-with-nexus-4.html" rel="alternate" type="text/html" title="I bought a Google Nexus 4 to replace my iPhone 4. There, I said it." /><published>2013-03-03T00:00:00-05:00</published><updated>2013-03-03T00:00:00-05:00</updated><id>/articles/replaced-iphone-4-with-nexus-4</id><content type="html" xml:base="/articles/replaced-iphone-4-with-nexus-4.html">&lt;p&gt;I bought a Google Nexus 4 to replace my iPhone 4. There, I said it. I feel like I have to justify myself a bit on this one.&lt;/p&gt;

&lt;p&gt;A few weeks back, my iPhone started acting strangely. It would freeze up for a few seconds, the Home button wasn’t working consistently, and the battery wasn’t really working great. The battery thing was expected — I mean, batteries don’t last forever — but the other things were just getting annoying. I knew it soon would be time to replace the phone, but getting an unlocked iPhone 5 was going to cost me around $800.&lt;/p&gt;

&lt;p&gt;Meanwhile, friends, co-workers, and family had been on me to take a look at the Nexus 4. “It’s vanilla ice cream whatever,” they would say. It was really starting to wear me down, and I have to admit my curiosity was getting the better of me. But the reason I pulled the trigger came down to money: the phone — which is unlocked — costs less than half what I’d spend to get a new iPhone. I figured, what’s the worst that could happen?&lt;/p&gt;

&lt;p&gt;OK, so let’s rip this Band-Aid off: I actually like the Nexus 4. I like it a lot. Sure, there are a couple things that bug me about it — but I’m definitely eating crow over my iOS fanaticism.&lt;/p&gt;

&lt;h2 id=&quot;things-i-like-about-my-nexus-4-in-order-of-appreciation&quot;&gt;Things I like about my Nexus 4 (in order of appreciation)&lt;/h2&gt;

&lt;h3 id=&quot;the-way-accounts-are-handled&quot;&gt;The way accounts are handled&lt;/h3&gt;

&lt;p&gt;Rather than handling logins in every app, Android centralizes (most?) accounts at the OS level. I added my three Google accounts, which meant that I automatically had my mail in the Gmail app, Maps had my recent search history, Chrome had my bookmarks, Google Reader had all my feeds — and I only had to log in once (per account). Facebook is there, too. Dropbox. Skype. Twitter. It’s all in one place, and apps can share the usage of those accounts without me having to type in my password over and over. Genius.&lt;/p&gt;

&lt;h3 id=&quot;links-open-in-chrome-always&quot;&gt;Links open in Chrome, always&lt;/h3&gt;

&lt;p&gt;I know iOS means well to embed the browser in each app, but it just makes more sense to me that tapping on links will open the web browser app. By doing that, I get to reuse cookies and sessions. I can leave things open in tabs and switch back to the app I was in. It’s closer to how multitasking works on your desktop OS.&lt;/p&gt;

&lt;h3 id=&quot;i-can-customize-anything&quot;&gt;I can customize anything&lt;/h3&gt;

&lt;p&gt;I’m loving that I can add widgets to the home screen. I can download apps that customize the LED for different notifications. I’ve read that I can replace nearly every aspect of the UI with something else, and then there’s the whole ‘rooting’ rabbit hole I can travel down, too. I like knowing that if something about the OS bugs me I can fix it.&lt;/p&gt;

&lt;h3 id=&quot;apps-can-do-more-throughout-the-os&quot;&gt;Apps can do more throughout the OS&lt;/h3&gt;

&lt;p&gt;Apps can do so much more in Android. Example: any app can add itself to the sharing options. I downloaded the Instapaper app, and now I can share links from Chrome or Google Reader directly into Instapaper — without resorting to a bookmarklet (which I always found a little hacky). Another example: Google Voice can take over text messages completely. That’s awesome. It can do phone calls via Google Voice, too, although I haven’t tried it.&lt;/p&gt;

&lt;h2 id=&quot;things-i-dont-like-about-my-nexus-4-in-order-of-annoyance&quot;&gt;Things I don’t like about my Nexus 4 (in order of annoyance)&lt;/h2&gt;

&lt;h3 id=&quot;no-home-button-button-on-the-side-is-hard-to-push&quot;&gt;No Home button, button on the side is hard to push&lt;/h3&gt;

&lt;p&gt;To turn my screen on, I have to push a button on the right side of the phone. It’s a small, hard-to-push button, and in order to push it with one hand I have to squeeze the phone in such a way that I often accidentally adjust the volume (which is in the same position on the left side of the phone). I don’t necessarily want a Home button on the front of the phone, but it sure would be nice to have something a &lt;em&gt;little&lt;/em&gt; easier to push.&lt;/p&gt;

&lt;h3 id=&quot;notifications-arent-quite-the-same&quot;&gt;Notifications aren’t quite the same&lt;/h3&gt;

&lt;p&gt;When my iPhone gets a text message, the screen lights up and I can read that text message. If I’m not near my phone or if it’s in my pocket, and I get a couple more text messages, they pile up on the screen. I can do this for other notifications as well, and it’s all configurable per app. It seems to me like a no brainer on how any phone should handle incoming notifications. Out of the box, my Nexus 4 does not do any of this. The best I get is a LED that lights up (sort of).&lt;/p&gt;

&lt;p&gt;I’ve been told that all of this is customizable via apps. Light Flow for the LED. SMS Popup for the iOS text message notification. And that’s fine, really — but it bugs me that these options aren’t built-in.&lt;/p&gt;

&lt;h3 id=&quot;i-need-to-customize-everything&quot;&gt;I need to customize everything&lt;/h3&gt;

&lt;p&gt;Out of the box, the OS feels unfinished. Not in a half-assed, Windows ME way, but in a “we expect you to tune this to your preferences” way. The first night I got my phone, I was fiddling with it for hours. It’s not just the notifications thing, either, but everything. There’s &lt;em&gt;so much&lt;/em&gt; to configure that it was overwhelming. I still feel like there’s an entire settings page somewhere in the phone that I haven’t seen yet, and the OCD side of me is really insecure about that. Sometimes, I just want my phone to be a phone, you know?&lt;/p&gt;

&lt;h3 id=&quot;the-speaker-is-really-quiet-vibration-is-wussy&quot;&gt;The speaker is really quiet, vibration is wussy&lt;/h3&gt;

&lt;p&gt;They designed the phone with the speaker on the back. When it’s sitting on a flat surface, I can’t hear it even if it’s turned to full volume. That’s just an engineering fail right there. And the vibrate feature is really weak. And because the screen doesn’t light up when I get notifications, it’s like this phone is designed for me to miss everything when it comes in.&lt;/p&gt;

&lt;h3 id=&quot;too-big-for-my-freakishly-tiny-hands&quot;&gt;Too big for my freakishly-tiny hands&lt;/h3&gt;

&lt;p&gt;My iPhone 4 fit perfectly in my left hand, allowing me to type and tap with my thumb, without once feeling like it was going to slip out of my palm. This Nexus 4 is too wide for one-handed usage. I can rest the phone against the bottom of my palm and angle it so that my thumb reaches most of the keyboard, but then my fingers are only resting on the back of the phone rather than gripping it. It wouldn’t take much for it to slip out of my hand altogether.&lt;/p&gt;

&lt;p&gt;Thing is, I never knew I had tiny hands before I started using this phone. It explains so much. No wonder I was lousy at bass guitar.&lt;/p&gt;</content><author><name>Ben Clark</name></author><category term="android" /><summary type="html">I bought a Google Nexus 4 to replace my iPhone 4. There, I said it. I feel like I have to justify myself a bit on this one.</summary></entry><entry><title type="html">How to unlock the power of Drupal’s system_settings_form()</title><link href="/articles/how-to-unlock-the-power-of-system_settings_form.html" rel="alternate" type="text/html" title="How to unlock the power of Drupal's system_settings_form()" /><published>2013-01-21T00:00:00-05:00</published><updated>2013-01-21T00:00:00-05:00</updated><id>/articles/how-to-unlock-the-power-of-system_settings_form</id><content type="html" xml:base="/articles/how-to-unlock-the-power-of-system_settings_form.html">&lt;p&gt;When building a module that requires a custom administration settings page, the &lt;code class=&quot;highlighter-rouge&quot;&gt;system_settings_form()&lt;/code&gt; is indispensible. It saves the contents of a form into system variables, and this frees developers from having to write their own &lt;code class=&quot;highlighter-rouge&quot;&gt;#submit&lt;/code&gt; callback for a form. This turns out to be a huge timesaver when building forms with simple things like checkboxes and select fields that don’t really need to be validated or transformed.&lt;/p&gt;

&lt;p&gt;But typically this function isn’t considered for more complex operations or data types beyond integers and strings. What if you want to save an array to a variable? Or what if you need to transform a string value containing a username into an integer user ID?&lt;/p&gt;

&lt;p&gt;Normally, these cases would require a custom &lt;code class=&quot;highlighter-rouge&quot;&gt;#submit&lt;/code&gt; callback, but I want to propose an alternative. I’m going to outline an approach to this problem that utilizes the &lt;code class=&quot;highlighter-rouge&quot;&gt;system_settings_form()&lt;/code&gt; to save transformed variables to the database. I’ll then follow up with two real-world implementations.&lt;/p&gt;

&lt;h2 id=&quot;the-problem&quot;&gt;The problem&lt;/h2&gt;

&lt;p&gt;It makes no sense to save a untransformed value to the variable, because that means you’ll just have to write a getter function and call it each time you want to use the variable for anything meaningful. That takes precious nanoseconds of page load time, and is completely pointless. Instead, you should take the time to do the expensive processing only when the variable is changed. Then, when it comes time to use your variable, you’re ready to go as soon as you call &lt;code class=&quot;highlighter-rouge&quot;&gt;variable_get()&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;(Plus if you’re slightly OCD like me, especially when it comes to the integrity of your data, it’s irritating to know that there’s ugliness living in your database that you’re going to have to clean up anytime you want to use it.)&lt;/p&gt;

&lt;p&gt;But why not just write your own &lt;code class=&quot;highlighter-rouge&quot;&gt;#submit&lt;/code&gt; callback that does the same thing? That’s a valid question, and I’ll preface my argument by saying that it’s going to boil down to a matter of taste. It’s my opinion that this is a cleaner approach to the problem. As with all things Drupal, there are multiple other ways to do this. There’s never only one right way with Drupal (but usually several absolutely wrong ways).&lt;/p&gt;

&lt;h3 id=&quot;it-means-code-portability&quot;&gt;It means code portability&lt;/h3&gt;

&lt;p&gt;With all your logic at the form element scope, you can move that form element into an entirely separate form, and it will work exactly the same. Furthermore, you can add the same form element to another form (for whatever reason) and keep it in your settings form, and reference the same value and validate callbacks.&lt;/p&gt;

&lt;p&gt;If you did all of this work inside a &lt;code class=&quot;highlighter-rouge&quot;&gt;#submit&lt;/code&gt; callback, you’d end up with duplicated logic at best, and at worst you might be stuck refactoring a form later on if you change the format of the variable.&lt;/p&gt;

&lt;h3 id=&quot;it-means-code-reusability&quot;&gt;It means code reusability&lt;/h3&gt;

&lt;p&gt;Consider the use of an autocomplete on a textfield (which I will demonstrate in my examples below). It’s conceivable that you would have this on more than just one form, and multiple times on the same form. By using the &lt;code class=&quot;highlighter-rouge&quot;&gt;#element_validate&lt;/code&gt; and &lt;code class=&quot;highlighter-rouge&quot;&gt;#value_callback&lt;/code&gt; approach, you only need to write two functions for an unlimited number of fields.&lt;/p&gt;

&lt;h2 id=&quot;the-method&quot;&gt;The method&lt;/h2&gt;

&lt;p&gt;All you need to do is add &lt;code class=&quot;highlighter-rouge&quot;&gt;#value_callback&lt;/code&gt; and &lt;code class=&quot;highlighter-rouge&quot;&gt;#element_validate&lt;/code&gt; to your form element.&lt;/p&gt;

&lt;p&gt;Start with a settings form that looks something like this:&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-php&quot; data-lang=&quot;php&quot;&gt;&lt;span class=&quot;cp&quot;&gt;&amp;lt;?php&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;D6MODULE_admin_settings_form&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;nv&quot;&gt;$form&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;'D6MODULE_variable1'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;array&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;
    &lt;span class=&quot;s1&quot;&gt;'#type'&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;'textfield'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;s1&quot;&gt;'#value_callback'&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;'D6MODULE_admin_variable1_value_callback'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;s1&quot;&gt;'#element_validate'&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;array&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;'D6MODULE_admin_variable1_validate'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt;
  &lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;system_settings_form&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$form&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;cp&quot;&gt;?&amp;gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;The value callback is used to convert the &lt;code class=&quot;highlighter-rouge&quot;&gt;#default_value&lt;/code&gt; — which will contain the &lt;em&gt;transformed&lt;/em&gt; variable’s value (e.g., an array, or a user ID) — into the format expected on the form:&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-php&quot; data-lang=&quot;php&quot;&gt;&lt;span class=&quot;cp&quot;&gt;&amp;lt;?php&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;D6MODULE_admin_variable1_value_callback&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$element&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$edit&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;FALSE&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;func_num_args&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;// No value yet, so use #default_value if available.
&lt;/span&gt;    &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;isset&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$element&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;'#default_value'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;])&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;is_numeric&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$element&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;'#default_value'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]))&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
      &lt;span class=&quot;c1&quot;&gt;// Convert internal value to user-friendly value.
&lt;/span&gt;      &lt;span class=&quot;nv&quot;&gt;$value&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;_D6MODULE_admin_variable1_transform_from_internal&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$element&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;'#default_value'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]);&lt;/span&gt;
      &lt;span class=&quot;c1&quot;&gt;// Return the transformed value to the form.
&lt;/span&gt;      &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$value&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;// If the #default_value is blank, return empty string here.
&lt;/span&gt;    &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;''&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
  &lt;span class=&quot;c1&quot;&gt;// For other calls to the value_callback, return the second parameter.
&lt;/span&gt;  &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$edit&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;cp&quot;&gt;?&amp;gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;The element validate callback is used to both validate the data (as needed) and in the event validation passes, to actually do the transformation and save it in the form via &lt;code class=&quot;highlighter-rouge&quot;&gt;form_set_value()&lt;/code&gt;:&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-php&quot; data-lang=&quot;php&quot;&gt;&lt;span class=&quot;cp&quot;&gt;&amp;lt;?php&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;D6MODULE_admin_variable1_validate&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$element&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$form_state&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;!&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;empty&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$element&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;'#value'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]))&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;// Check that the user-friendly value is valid (optional).
&lt;/span&gt;    &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;!&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;_D6MODULE_admin_variable1_do_validation&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$element&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;'#value'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]))&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
      &lt;span class=&quot;c1&quot;&gt;// Failed validation, return form error.
&lt;/span&gt;      &lt;span class=&quot;nx&quot;&gt;form_error&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$element&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;t&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;'Variable was not valid.'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;));&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;else&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
      &lt;span class=&quot;c1&quot;&gt;// If passed validation, convert user-friendly value to internal value.
&lt;/span&gt;      &lt;span class=&quot;nv&quot;&gt;$value&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;_D6MODULE_admin_variable1_transform_to_internal&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$element&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;'#default_value'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]);&lt;/span&gt;
      &lt;span class=&quot;c1&quot;&gt;// Set the value on the form.
&lt;/span&gt;      &lt;span class=&quot;nx&quot;&gt;form_set_value&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$element&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$value&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$form_state&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
  &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;cp&quot;&gt;?&amp;gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;h2 id=&quot;example-implementations&quot;&gt;Example implementations&lt;/h2&gt;

&lt;p&gt;Here are some example implementations of this method.&lt;/p&gt;

&lt;h3 id=&quot;explode-textarea-contents-into-an-array&quot;&gt;Explode textarea contents into an array&lt;/h3&gt;

&lt;noscript&gt;&lt;pre&gt;400: Invalid request
&lt;/pre&gt;&lt;/noscript&gt;
&lt;script src=&quot;https://gist.github.com/4590019.js&quot;&gt; &lt;/script&gt;

&lt;h3 id=&quot;user-autocomplete-on-a-textfield-but-save-user-id&quot;&gt;User autocomplete on a textfield but save user ID&lt;/h3&gt;

&lt;noscript&gt;&lt;pre&gt;400: Invalid request
&lt;/pre&gt;&lt;/noscript&gt;
&lt;script src=&quot;https://gist.github.com/4590024.js&quot;&gt; &lt;/script&gt;</content><author><name>Ben Clark</name></author><category term="drupal" /><category term="6.x" /><category term="drupal tips" /><category term="howto" /><summary type="html">When building a module that requires a custom administration settings page, the system_settings_form() is indispensible. It saves the contents of a form into system variables, and this frees developers from having to write their own #submit callback for a form. This turns out to be a huge timesaver when building forms with simple things like checkboxes and select fields that don’t really need to be validated or transformed.</summary></entry><entry><title type="html">Farewell, Linode! Or, why I should be learning new things</title><link href="/articles/farewell-linode.html" rel="alternate" type="text/html" title="Farewell, Linode! Or, why I should be learning new things" /><published>2013-01-20T00:00:00-05:00</published><updated>2013-01-20T00:00:00-05:00</updated><id>/articles/farewell-linode</id><content type="html" xml:base="/articles/farewell-linode.html">&lt;p&gt;&lt;img class=&quot;post-image img-polaroid pull-left&quot; src=&quot;/files/linode-end-of-an-era.png&quot; alt=&quot;Linode, end of an era&quot; /&gt;&lt;/p&gt;

&lt;p&gt;My Linode account was activated November 17, 2009, and I shut it down January 14, 2013. During that time, the longest stretch of uptime was 545 days.&lt;/p&gt;

&lt;p&gt;I originally switched to Linode from shared hosting on Dreamhost. I really enjoyed the freedom of being able to run my preferred distro (Debian) and call my own shots regarding what tools I used (Lighttpd, Varnish, memcached). So I set everything up, threw some sites on there, and locked everything down — and then left it mostly alone for 3 years.&lt;/p&gt;

&lt;p&gt;Here’s the thing: in those 3 years, a lot changed. At some point when I wasn’t looking, Amazon Web Services matured into something really accessible and affordable, especially for people like me who don’t need much in the way of RAM or CPU for their vanity domains, but who do like to tinker with server-level things. Once I realized I was behind the curve with my VPS, I was kicking myself for being so complacent and loyal to that paradigm.&lt;/p&gt;

&lt;p&gt;A new year’s resolution is as good of a reason as any to take on a new project, so shortly after 2013 began, I began to migrate my domains away from my Linode to an EC2 instance. Then, I switched my nameservers away from Linode this past weekend, thus signaling the end of a very satisfied 3-year term.&lt;/p&gt;

&lt;h2 id=&quot;loyalty-shouldnt-keep-you-from-learning-new-things&quot;&gt;Loyalty shouldn’t keep you from learning new things&lt;/h2&gt;

&lt;p&gt;Part of the reason I didn’t pay much attention to alternatives to my VPS was that I was a satisfied customer. Although I recognized that cloud computing services and platforms as service were becoming things people actually used, I didn’t want to spend the time and effort to learn something new if my VPS still met all my needs. Besides, I had plenty of other things going on career-wise to keep my brain occupied.&lt;/p&gt;

&lt;p&gt;But for someone who identifies as a sysadmin type, I did a disservice to myself by being loyal to my VPS for so long. Don’t get me wrong, I’m not advocating eschewing loyalty in the pursuit of marginally lower prices or anything like that. Loyalty is generally good. But in this case, loyalty became an excuse I used to avoid learning new skills, and I missed out on emerging industry trends.&lt;/p&gt;

&lt;p&gt;This got me thinking: I’m a loyal customer of Drupal, too. I’ve been working with Drupal since 2006. That’s six-going-on-seven years of being a full-time Drupal developer, and my focus on Drupal development has come at the expense of my not learning new frameworks and languages.&lt;/p&gt;

&lt;h2 id=&quot;we-cant-afford-to-stand-still-for-very-long&quot;&gt;We can’t afford to stand still for very long&lt;/h2&gt;

&lt;p&gt;Here’s the hard truth, folks. Developers — and all corners of the information sector, really — can’t afford to stand still for very long. Every semester, there are kids graduating college who have been brought up in an era of X paradigm and Y framework. They’re going to be kickstarting new projects with incubators and crowdsourcing and whatever other thing that is trending right now.&lt;/p&gt;

&lt;p&gt;If you’re not staying on top of things, or if you’re not at least dipping your toes in the shallow end of whatever, then you’re running the risk of becoming out-of-date. These kids coming up will lap shortsighted, loyal developers. The longer it takes for us to notice, the worse it gets.&lt;/p&gt;

&lt;p&gt;Sure, you can be the type that complains about it, who says X paradigm is just a buzzword, Y framework is a fad — &lt;em&gt;“just you wait, technology is cyclical”&lt;/em&gt; — but you’ll reach the point where what you’re loyal to is no longer relevant. And whenever you eventually realize that you picked loyalty over professional development, are you prepared to invest the time and effort to change course?&lt;/p&gt;

&lt;h2 id=&quot;whats-next&quot;&gt;What’s next?&lt;/h2&gt;

&lt;p&gt;I’ll come clean: learning more about AWS was eye-opening. I recognize that I’ve only just discovered the tip of the iceberg, too. There’s the whole PAAS sphere that I need to explore, not to mention all the supplemental tools and products that exist. I’m excited to learn more about all of it.&lt;/p&gt;

&lt;p&gt;I’m also nervous that my web development skills, which were state-of-the-art 6 years ago, are now showing their age. I’ve set a goal for myself in 2013 to not be loyal to any one technology merely for loyalty’s sake.&lt;/p&gt;

&lt;p&gt;I want to stay relevant (and employable!) for a long time to come, and I think being open to new things is the only way to ensure that. I’m reminded of the sage words of Johnny 5 from &lt;em&gt;Short Circuit&lt;/em&gt;: “Need input. Input.” I do feel as though I need to play catch-up, but I’m equal to the task, and in truth, energized by it.&lt;/p&gt;</content><author><name>Ben Clark</name></author><category term="linode" /><category term="vps" /><category term="benclark.com" /><category term="aws" /><category term="ec2" /><category term="drupal" /><category term="work" /><summary type="html"></summary></entry><entry><title type="html">The four stages of the Drupal developer life cycle</title><link href="/articles/the-four-stages-of-the-drupal-developer-life-cycle.html" rel="alternate" type="text/html" title="The four stages of the Drupal developer life cycle" /><published>2012-11-17T00:00:00-05:00</published><updated>2012-11-17T00:00:00-05:00</updated><id>/articles/the-four-stages-of-the-drupal-developer-life-cycle</id><content type="html" xml:base="/articles/the-four-stages-of-the-drupal-developer-life-cycle.html">&lt;p&gt;I’ve been working in the Drupal ecosystem for a while now, and I’ve noticed there’s a predictable life cycle of the Drupal developer. We all know the stages. Drupal developers start out hacking away at the theme layer. Then, they start writing their own modules. Still others start contributing those modules back to the community.&lt;/p&gt;

&lt;p&gt;Eventually, those who survive the earlier stages grow to be good Drupal citizens, working as professionals and contributing back to the community in a meaningful, productive way. As critical as this stage is for the well-being of all developers, to achieve it requires self-awareness and discipline.&lt;/p&gt;

&lt;h2 id=&quot;we-were-all-beginners-once&quot;&gt;We were all beginners once&lt;/h2&gt;

&lt;p&gt;Beginners first approach Drupal like a closed box. It’s not something to be tweaked or hacked. Instead, they scour Drupal.org looking for modules they might need. They enable modules indiscriminately. The complexity of their projects increase exponentially as they install more and more modules, and they begin to complain about Drupal’s “learning curve.” Their attention may turn to aesthetics: “Hey, my site looks pretty ugly with all of these blocks and tabs on the page!” Or their attention turns to a limitation of one module or another: “I hate how limited Views is! Ugh, seriously, how hard is it to create a view of related nodes?”&lt;/p&gt;

&lt;p&gt;At this point, the beginner starts to peel away the outer layer of the closed box. They search Drupal.org for example code, and begin making the beginner’s favorite dish: copy pasta. They start editing &lt;code class=&quot;highlighter-rouge&quot;&gt;node.tpl.php&lt;/code&gt; and &lt;code class=&quot;highlighter-rouge&quot;&gt;template.php&lt;/code&gt;. Their first theme may or may not be named &lt;code class=&quot;highlighter-rouge&quot;&gt;zen_starterkit&lt;/code&gt;. They find a thread that says to throw some PHP in a Views argument, maybe a &lt;code class=&quot;highlighter-rouge&quot;&gt;node_load(arg(1))&lt;/code&gt; to get an array of terms. They’re well-intentioned but they lack the understanding of what it is they’re copying and what it is they’re overriding. They’re ignorant of the “Drupal way.”&lt;/p&gt;

&lt;p&gt;Thing is, Drupal beginners are great people. We need more of them, especially if we’re to sustain our ecosystem. Yes, we need them to learn more, and learn more faster, but they won’t get to where we need them without our help. We must mentor our beginners as best we can, within Drupal.org issue queues and IRC chat rooms, and when they’re our co-workers and subordinates.&lt;/p&gt;

&lt;p&gt;Don’t lie to yourself, either: we were &lt;em&gt;all&lt;/em&gt; beginners once.&lt;/p&gt;

&lt;h2 id=&quot;intermediate-developers-are-fearless&quot;&gt;Intermediate developers are fearless&lt;/h2&gt;

&lt;p&gt;Assuming the beginner is given a chance to grow in a healthy environment, the natural progression is to become an intermediate Drupal developer.&lt;/p&gt;

&lt;p&gt;At this stage, the developer approaches Drupal like a blank piece of paper. “A robust system of hooks!” they say with enthusiasm. “I shall use these to make modules for myself!” They’re a child playing with Legos for the first time. There is an overwhelming temptation to build anything and everything with these new building blocks — as they understand them. They’re fearless.&lt;/p&gt;

&lt;p&gt;Unfortunately, this means they’re going to write a module when it’s not strictly necessary. They’re going to write a module that changes the page title for node pages. They’re going to write a module that adds some metatags to the &lt;code class=&quot;highlighter-rouge&quot;&gt;&amp;lt;head /&amp;gt;&lt;/code&gt;. They’re going to write a module that sends an email to a mailing list every time a node is saved or updated.&lt;/p&gt;

&lt;p&gt;This is arguably the stage most dangerous to the rest of us. They could be at this stage here indefinitely, masquerading as professional developers. They could do a lot of damage when left unsupervised on a project. And we’re the ones that are inevitably tasked with reviewing their work and fixing it. (I shudder to think, what if we made the mistake of agreeing to a fixed fee before discovering their code soup?)&lt;/p&gt;

&lt;p&gt;But there is hope, because there are some in this group who have raw enthusiasm for Drupal as a platform. They want to learn more, write more code, and build with Drupal more effectively. All they’re lacking is the holistic understanding of the community at large, how it all works together and how to leverage existing work to avoid reengineering the wheel&lt;sup id=&quot;fnref:1&quot;&gt;&lt;a href=&quot;#fn:1&quot; class=&quot;footnote&quot;&gt;1&lt;/a&gt;&lt;/sup&gt;.&lt;/p&gt;

&lt;h2 id=&quot;advanced-developers-call-it-dee-dot-oh&quot;&gt;Advanced developers call it “dee dot oh”&lt;/h2&gt;

&lt;p&gt;The intermediate developer graduates to the next stage, advanced Drupal developer, when they recognize that Drupal isn’t a blank piece of paper, but a rich tapestry. While the thrill of writing a bespoke module remains, there is now also wisdom and the knowledge that there’s no sense in writing their own &lt;code class=&quot;highlighter-rouge&quot;&gt;auto_nodetitle&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Advanced Drupal developers recognize that there’s a larger community of developers out there, so before starting with a blank &lt;code class=&quot;highlighter-rouge&quot;&gt;.module&lt;/code&gt; file, they’re going to first check for pre-existing modules and APIs that they can leverage. They’re going to pay attention to best practices and security concerns — they know better than to output text without running it through &lt;code class=&quot;highlighter-rouge&quot;&gt;check_plain()&lt;/code&gt;. They might even know to leverage things like the Cache API and the Batch API to be more effective and efficient. They’ve written Views handlers, Panels plugins, and Context reactions.&lt;/p&gt;

&lt;p&gt;At first glance, you might mistake this developer as an expert. In a lot of ways, you’d be right. This developer went to Drupalcon, maybe even sat in on a core conversation, or attended a BoF or two. They call Drupal.org “dee dot oh.” They also participate in local community events, and they’re almost certainly employed as a full-time Drupal developer&lt;sup id=&quot;fnref:2&quot;&gt;&lt;a href=&quot;#fn:2&quot; class=&quot;footnote&quot;&gt;2&lt;/a&gt;&lt;/sup&gt;. This type of developer knows the core API inside and out. They might boast that they’ve been working with Drupal since 4.7, and might claim that they’ve written modules for all major releases.&lt;/p&gt;

&lt;p&gt;While all of these observations are correct, don’t put this kind of developer on a pedestal. They are far from perfect.&lt;/p&gt;

&lt;p&gt;Let’s say they find a contrib module out in the wild that suits their needs. They enable it and find out that it’s buggy, or it doesn’t match their use case exactly. Or even more arbitrarily, it just doesn’t fit their expectations for how it “should be” implemented. Their next thought is, “oh well, I guess I’ll have to write my own!” They expect perfection — or at least &lt;em&gt;likemindedness&lt;/em&gt; — from all contrib module authors, and when faced with the alternative, they choose to eschew the community and write their own solution.&lt;/p&gt;

&lt;p&gt;The all-or-nothing position is dangerous for a Drupal developer because that implies that skepticism about the ability for existing modules to meet expectations is their default position. The developer works within a bubble even while participating in the community. Their position boils down to “I trust my implementation choices above all else.” They dismiss the intellectual capital invested by the maintainers as inconsequential.&lt;/p&gt;

&lt;p&gt;From a practical standpoint, there’s a laundry list of reasons not to do this. They’re going to re-introduce bugs or security flaws that others have already encountered and fixed in the so-considered inferior solution. They’re going to leave out features that they might want down the road, not out of malice but because they simply didn’t think of them.&lt;/p&gt;

&lt;p&gt;From a social standpoint, these developers will never have a chance to discover other developers are in fact likeminded. After all, it’s not unprecedented that a contrib module would have a complete rewrite in the &lt;code class=&quot;highlighter-rouge&quot;&gt;-dev&lt;/code&gt; branch for the exact purpose of trying out a new design.&lt;/p&gt;

&lt;p&gt;Working in a bubble is a terribly narrow-minded approach to open-source software development in general, but it is a cancer within the Drupal community because &lt;em&gt;you can get away with it&lt;/em&gt;. There is no such thing as the “module police.” One developer can easily fork another developer’s module to add support for some feature they need on that one project, and nobody would ever know. Sure, maybe someday another developer will be asked to help out on that project and find the forked module, but even then there’s no guarantee that the changes will be merged back in to the community. It’s selfish, plain and simple — and completely unenforceable.&lt;/p&gt;

&lt;h2 id=&quot;good-drupal-citizens-police-themselves&quot;&gt;Good Drupal citizens police themselves&lt;/h2&gt;

&lt;p&gt;Self-policing is what allows the advanced developer to become a good Drupal citizen. They stop working in a bubble. They begin to contribute their efforts back to the community in such a way that acknowledges existing work rather than dismissing it.&lt;/p&gt;

&lt;p&gt;They’re not afraid to submit a patch for a new feature on an existing module. They wait for their idea to be reviewed &amp;amp; tested by the community (RTBC), and they keep an open mind about their approach when it needs work. If the patch is rejected by the module maintainer, they find a constructive workaround.&lt;/p&gt;

&lt;p&gt;When the good Drupal citizen writes a new module, it is to compliment existing APIs or fill legitimate gaps. They don’t make assumptions about use cases. They write unitasker modules that don’t make assumptions about the theme layer or menu structure. They recognize that some sites will be built with Context and others Panels, and yet others will use core blocks and node templates.&lt;/p&gt;

&lt;p&gt;What they &lt;em&gt;don’t&lt;/em&gt; do is work in a bubble. Anything they do is done with the assumption that it will be shared with the community in some way.&lt;/p&gt;

&lt;p&gt;The community has a right to know what you’ve done and how you’ve done it. Whether it’s shared by submitting a patch, contributing to a 10-page comment thread in an issue queue, writing a blog post, or adding a new module on Drupal.org, good Drupal citizens contribute back. It’s open, it’s transparent. If you’re a PHP genius, the world deserves to know; if you’re barely competent, the world deserves to know that, too. If you won’t stand by your work, then we don’t want you in our community. Stop believing that you’re the only one who will want that feature you’re developing. Stop yourself from thinking that your implementation choices are without flaw.&lt;/p&gt;

&lt;h2 id=&quot;we-all-benefit-from-participation&quot;&gt;We all benefit from participation&lt;/h2&gt;

&lt;p&gt;The Drupal community is lacking a formal peer review process. Participation does not require certification. There are no classes to take. You can start as a beginner and work your way through that intermediate stage of knowing just enough to get the job done, and you can stay employed and do a decent-enough job to never need to grow beyond that.&lt;/p&gt;

&lt;p&gt;The measure of a good Drupal citizen, then, is self-policing. Before you fork that module for your own needs, consider how you can integrate your needs with the existing work and allow your Drupal developer peers to review it. Share your expertise with beginners. Encourage your intermediate co-workers to learn new APIs. Be the mentor that you never had because you started with Drupal back when it was just you and Dries.&lt;/p&gt;

&lt;p&gt;Bottom line, recognize that we all benefit from your contribution, however big or small, and that you benefit from our collective expertise. No single developer knows more about Drupal than the community does, and yet, there wouldn’t be a community if people didn’t share their knowledge with others.&lt;/p&gt;

&lt;div class=&quot;footnotes&quot;&gt;
  &lt;ol&gt;
    &lt;li id=&quot;fn:1&quot;&gt;
      &lt;p&gt;I promised myself I wouldn’t use that phrase. Damn. &lt;a href=&quot;#fnref:1&quot; class=&quot;reversefootnote&quot;&gt;&amp;#8617;&lt;/a&gt;&lt;/p&gt;
    &lt;/li&gt;
    &lt;li id=&quot;fn:2&quot;&gt;
      &lt;p&gt;… unless they’re (a) completely unaware of the demand for developers, or (b) completely un-hireable for some reason unrelated to their knowledge of Drupal. Good grief, they should at least be part-time contractors. There’s gold in them there hills! &lt;a href=&quot;#fnref:2&quot; class=&quot;reversefootnote&quot;&gt;&amp;#8617;&lt;/a&gt;&lt;/p&gt;
    &lt;/li&gt;
  &lt;/ol&gt;
&lt;/div&gt;</content><author><name>Ben Clark</name></author><category term="drupal" /><summary type="html">I’ve been working in the Drupal ecosystem for a while now, and I’ve noticed there’s a predictable life cycle of the Drupal developer. We all know the stages. Drupal developers start out hacking away at the theme layer. Then, they start writing their own modules. Still others start contributing those modules back to the community.</summary></entry><entry><title type="html">4 things I learned about myself by working from home</title><link href="/articles/4-things-i-learned-about-myself-by-working-from-home.html" rel="alternate" type="text/html" title="4 things I learned about myself by working from home" /><published>2012-06-06T00:00:00-04:00</published><updated>2012-06-06T00:00:00-04:00</updated><id>/articles/4-things-i-learned-about-myself-by-working-from-home</id><content type="html" xml:base="/articles/4-things-i-learned-about-myself-by-working-from-home.html">&lt;p&gt;I started working from home when I joined Jackson River, and after a week or two of settling in, I realized four things about myself. As it turns out, it’s not hard at all to get into “work mode.” And overall, I’m a lot more productive. But there are downsides, too: I find that it’s hard to stop working at the end of the day. And after over a year of working from home, I felt cut off from the world.&lt;/p&gt;

&lt;h2 id=&quot;the-good&quot;&gt;The good&lt;/h2&gt;

&lt;h3 id=&quot;its-not-hard-to-switch-into-work-mode&quot;&gt;It’s not hard to switch into work mode&lt;/h3&gt;

&lt;p&gt;Even though I’m in my home, I’ve found it’s not that hard to switch into work mode as soon as I sit down at my desk in the morning. Although I now have a dedicated room for my home office, I was working from a kitchen table for the first month and a half at Jackson River.&lt;/p&gt;

&lt;p&gt;Don’t assume that my ability to switch into work mode is just because I can walk into my office, close the door, and shut out everything that might remind me of being at home. Instead, it’s all about setting ground rules and sticking to them.&lt;/p&gt;

&lt;p&gt;I have to sit in a chair—a proper desk chair. If I’m on the couch, it doesn’t feel like I’m “at work,” and if I find myself leaned too far back, my neck starts to ache after only a few minutes. The chair can be comfortable, but not too comfortable, like an office chair you’d get in a real office. I think more than anything else, sitting like I’m “at work” tricks my brain enough to forget that my TV or nap-inducing couch lies just outside my peripheral vision.&lt;/p&gt;

&lt;p&gt;Music helps a lot, at least it does for me. I’ll set up iTunes DJ to play from a genre playlist or just randomly from my library, and I’m three hours into a series of tickets before I realize it. But I can’t watch TV. This sets off a chain reaction of laziness. I also struggle with podcasts or talk radio, although I’m sure others can multitask just fine.&lt;/p&gt;

&lt;p&gt;I also set ground rules with my family and friends: when I’m at work, I’m at work, and there are rarely exceptions to that. I can’t be relied on to do household chores (except sign for packages, small things that don’t detract too much from my concentration). Sure, I can answer the phone, but I can’t talk socially. I can make my own lunch, sure, but watching over a roast in the oven might be too much. This might make me seem uptight, but I need that level of understanding and respect for my work time. It also helps me simulate the physical distance from home life that an office commute provides. I have been fortunate in that everyone in my life had been very understanding of this rule since day one.&lt;/p&gt;

&lt;h3 id=&quot;im-a-lot-more-productive&quot;&gt;I’m a lot more productive&lt;/h3&gt;

&lt;p&gt;I’ll be frank, some days at my previous office jobs were spent just making sure that the desk chair didn’t float away. (It never did.) So yes, there was a lazy component to the job to begin with. But I would say the most significant barrier to productivity in my old office job was the open door. People would just come in with their help desk issues and I would stop what I was working on to help them. After the first year of this, I mostly stopped working on web development because I was so discouraged by the interruptions.&lt;/p&gt;

&lt;p&gt;I think the secret to productivity when working from home is accountability. I’m accountable for the hours I spend, the tickets that I accept and complete, and the emails I send back and forth with clients. I need to maintain a certain level of output, and this keeps me from remembering that I could easily pick up a guitar and start jamming away.&lt;/p&gt;

&lt;h2 id=&quot;the-bad&quot;&gt;The bad&lt;/h2&gt;

&lt;p&gt;There are fewer barriers to productivity when working from home, provided that you’ve set up your environment correctly. So at first, there will be a temptation to take this too far—to find out just how productive you can be, to prove to your friends that, yes, where you work is a real job, and so forth. My first six months at Jackson River were a blur of completed tickets and a real productivity binge.&lt;/p&gt;

&lt;h3 id=&quot;its-hard-to-stop-working-at-the-end-of-the-day&quot;&gt;It’s hard to stop working at the end of the day&lt;/h3&gt;

&lt;p&gt;“Honey, it’s almost 9 PM, can you please come downstairs and join me for dinner?” “In a minute, I want to finish this one last ticket.”&lt;/p&gt;

&lt;p&gt;Congratulations, you’ve created an environment of productivity and intense concentration—in your home. One consequence is that when 6 PM rolls around, there’s always one last task, one last email to write, one last ticket to return to the client. How do you turn off your brain when you’re in a uninterrupted headspace that comes so rarely in every other office job you’ve ever had?&lt;/p&gt;

&lt;p&gt;I don’t have an answer for you, unfortunately. I tried setting a “work curfew,” but the first time you break curfew for an urgent “our website is offline” issue, you’ve rendered it meaningless. Working from home brought out a side of myself that, frankly, I don’t find especially charming: I am now a workaholic.&lt;/p&gt;

&lt;p&gt;Just be mindful that tomorrow will be yet another full work day, and the tickets you’re working on tonight at 11 PM won’t be seen by the client until 9 AM at the earliest. Any tickets you complete after hours are just going to be replaced by new tickets tomorrow. I’m not suggesting you blow off work, or set a slower than realistic pace for yourself, because that benefits no one. But if this was an office job, you’d be eyeing the people leaving the building at 5:30 (because the boss left at 5) and wondering whether finishing two more tickets is worth coming home so late.&lt;/p&gt;

&lt;h3 id=&quot;i-felt-cut-off-from-the-world&quot;&gt;I felt cut off from the world&lt;/h3&gt;

&lt;p&gt;In the first six months of my job, I found that I wasn’t leaving the house except on the weekends. We would run our errands on Saturdays or Sundays and I would have enough lunch food to last me through Friday, and I just didn’t have a need to leave the house. Not healthy, not one bit.&lt;/p&gt;

&lt;p&gt;One perk about a commute is that you’re out in the world, experiencing it with other people. You go to an office full of people, you go out to lunch with other people, you’re participating in society in a physical, tangible way. Yes, the 45 minutes on the train or the 2 hours in traffic are a real drag, but it’s &lt;em&gt;real.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Again, I don’t really have a solution to offer. If you’re going to be working from home, then you need to make time to leave the house. I never really figured this one out for myself, and Jackson River opened a DC office just as I was reaching my breaking point. If you have lunch options that are walkable on a nice day, take advantage of that. Is there a park nearby that you can walk around in the mornings, evenings, or midday? Aside from being an excuse to walk away from the computer (and avoid the workaholic mentality), clearing my head with a good neighborhood walk usually helps me solve a tricky software bug or come up with a new approach to a design problem that has been plaguing the team.&lt;/p&gt;

&lt;h2 id=&quot;overall-i-give-it-a-b&quot;&gt;Overall, I give it a B+&lt;/h2&gt;

&lt;p&gt;Working from home can be a real enjoyable experience if you do it right, and it’s a lot easier to be a productive worker at home if you approach it with the right attitude and preparation. I think it’s a win-win if you go in with eyes wide open. There are pitfalls to watch out for, certainly, but overall it’s a manageable, positive experience.&lt;/p&gt;</content><author><name>Ben Clark</name></author><category term="work" /><category term="virtual work" /><category term="telecommuting" /><category term="work ethic" /><summary type="html">I started working from home when I joined Jackson River, and after a week or two of settling in, I realized four things about myself. As it turns out, it’s not hard at all to get into “work mode.” And overall, I’m a lot more productive. But there are downsides, too: I find that it’s hard to stop working at the end of the day. And after over a year of working from home, I felt cut off from the world.</summary></entry><entry><title type="html">How to change Mac OS X Terminal setting per SSH host</title><link href="/articles/how-to-change-mac-os-x-terminal-setting-per-ssh-host.html" rel="alternate" type="text/html" title="How to change Mac OS X Terminal setting per SSH host" /><published>2012-01-05T00:00:00-05:00</published><updated>2012-01-05T00:00:00-05:00</updated><id>/articles/how-to-change-mac-os-x-terminal-setting-per-ssh-host</id><content type="html" xml:base="/articles/how-to-change-mac-os-x-terminal-setting-per-ssh-host.html">&lt;p&gt;So, call me crazy, but when I’ve got a dozen Terminal windows and tabs open at once, and they’re all ssh’d to different boxes, it gets hard to remember which host is which. I end up checking the prompt or doing something like &lt;code class=&quot;highlighter-rouge&quot;&gt;hostname&lt;/code&gt; to remember where I am. So, I found a way to automatically switch the Terminal settings to color-code my SSH hosts.&lt;/p&gt;

&lt;p&gt;There are two steps. First, we need to create an AppleScript script&lt;sup id=&quot;fnref:1&quot;&gt;&lt;a href=&quot;#fn:1&quot; class=&quot;footnote&quot;&gt;1&lt;/a&gt;&lt;/sup&gt; that will actually do the switching of the Terminal settings. Save this code somewhere memorable, as we will refer to it later on.&lt;/p&gt;

&lt;noscript&gt;&lt;pre&gt;400: Invalid request
&lt;/pre&gt;&lt;/noscript&gt;
&lt;script src=&quot;https://gist.github.com/3059850.js&quot;&gt; &lt;/script&gt;

&lt;p&gt;The next step is to update your &lt;code class=&quot;highlighter-rouge&quot;&gt;ssh_config&lt;/code&gt; (normally &lt;code class=&quot;highlighter-rouge&quot;&gt;~/.ssh/config&lt;/code&gt;). We’re going to use the &lt;code class=&quot;highlighter-rouge&quot;&gt;LocalCommand&lt;/code&gt; directive to trigger our AppleScript script after the host connects. We also need to permit that directive via &lt;code class=&quot;highlighter-rouge&quot;&gt;PermitLocalCommand yes&lt;/code&gt;. It’s disabled by default, presumably for security reasons — so only enable it per host, not globally.&lt;/p&gt;

&lt;p&gt;Add something like this for each host you want to have color-coded, along with the Terminal setting name to use:&lt;/p&gt;

&lt;div class=&quot;highlighter-rouge&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;Host example.com
  PermitLocalCommand yes
  LocalCommand osascript %d/scripts/set_term_settings.scpt &quot;Homebrew&quot;
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;

&lt;p&gt;Replace &lt;code class=&quot;highlighter-rouge&quot;&gt;example.com&lt;/code&gt; with your host. Refer to the &lt;a href=&quot;http://compute.cnr.berkeley.edu/cgi-bin/man-cgi?ssh_config+4&quot;&gt;ssh man page&lt;/a&gt; for escape character substitutions — I’m using &lt;code class=&quot;highlighter-rouge&quot;&gt;%d&lt;/code&gt; here for my local user’s home directory. (Oh, and make sure that the &lt;code class=&quot;highlighter-rouge&quot;&gt;osascript&lt;/code&gt; command actually works by running it on the command line first.)&lt;/p&gt;

&lt;p&gt;You can add your own Terminal settings, or use the built-in ones. You can also tweak the script to set properties individually rather than using a setting name. I’m using this simply to change the background color and opacity, but you could go nuts and set any other property in the setting. This script could be pretty powerful depending on how you want to use it.&lt;/p&gt;

&lt;div class=&quot;footnotes&quot;&gt;
  &lt;ol&gt;
    &lt;li id=&quot;fn:1&quot;&gt;
      &lt;p&gt;“AppleScript script” looks redundant, but “AppleScript” by itself looks weird, too. Not sure what the standard is, but I’m going with redundant. &lt;a href=&quot;#fnref:1&quot; class=&quot;reversefootnote&quot;&gt;&amp;#8617;&lt;/a&gt;&lt;/p&gt;
    &lt;/li&gt;
  &lt;/ol&gt;
&lt;/div&gt;</content><author><name>Ben Clark</name></author><category term="applescript" /><category term="howto" /><category term="mac" /><category term="mac os x" /><category term="ssh" /><category term="terminal" /><category term="tips" /><summary type="html">So, call me crazy, but when I’ve got a dozen Terminal windows and tabs open at once, and they’re all ssh’d to different boxes, it gets hard to remember which host is which. I end up checking the prompt or doing something like hostname to remember where I am. So, I found a way to automatically switch the Terminal settings to color-code my SSH hosts.</summary></entry></feed>
