From cac4d98de2d3b70e230e9c808579edb1b0567046 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Jonathan=20Ara=C3=B1a=20Cruz?= <jonhattan@faita.net>
Date: Sun, 20 May 2012 18:37:54 +0200
Subject: [PATCH 1/3] Extend engines subsystem to allow for parametrized engines.

---
 commands/core/help.drush.inc |    1 +
 commands/pm/pm.drush.inc     |    4 +---
 includes/bootstrap.inc       |    1 +
 includes/command.inc         |   29 ++++++++++++++++++++---------
 includes/drush.inc           |    8 ++++++--
 includes/engines.inc         |   28 ++++++++++++++++++++++++++++
 6 files changed, 57 insertions(+), 14 deletions(-)
 create mode 100644 includes/engines.inc

diff --git a/commands/core/help.drush.inc b/commands/core/help.drush.inc
index fae1e31..2fcd968 100644
--- a/commands/core/help.drush.inc
+++ b/commands/core/help.drush.inc
@@ -47,6 +47,7 @@ function drush_print_help($command) {
   // Give commandfiles an opportunity to add examples and options to the command.
   drush_bootstrap_max(DRUSH_BOOTSTRAP_DRUPAL_SITE);
   drush_command_invoke_all_ref('drush_help_alter', $command);
+  drush_engine_type_help_alter($command);
 
   drush_print(wordwrap(implode("\n", $help), drush_get_context('DRUSH_COLUMNS', 80)));
   drush_print();
diff --git a/commands/pm/pm.drush.inc b/commands/pm/pm.drush.inc
index 5401032..ff6ef13 100644
--- a/commands/pm/pm.drush.inc
+++ b/commands/pm/pm.drush.inc
@@ -1526,9 +1526,7 @@ function drush_pm_include_version_control($directory = '.') {
     return drush_set_error('DRUSH_PM_NO_VERSION_CONTROL', dt('No valid version control or backup engine found (the --version-control option was set to "!version-control").', array('!version-control' => $version_control)));
   }
 
-  $instance = drush_include_engine('version_control', $version_control);
-  $instance->engine = $engine;
-  return $instance;
+  return drush_include_engine('version_control', $version_control);
 }
 
 /**
diff --git a/includes/bootstrap.inc b/includes/bootstrap.inc
index c13db27..3ada9d1 100644
--- a/includes/bootstrap.inc
+++ b/includes/bootstrap.inc
@@ -980,6 +980,7 @@ function drush_bootstrap_prepare() {
   require_once DRUSH_BASE_PATH . '/includes/environment.inc';
   require_once DRUSH_BASE_PATH . '/includes/command.inc';
   require_once DRUSH_BASE_PATH . '/includes/drush.inc';
+  require_once DRUSH_BASE_PATH . '/includes/engines.inc';
   require_once DRUSH_BASE_PATH . '/includes/backend.inc';
   require_once DRUSH_BASE_PATH . '/includes/batch.inc';
   require_once DRUSH_BASE_PATH . '/includes/context.inc';
diff --git a/includes/command.inc b/includes/command.inc
index 4b4ad33..9ef71c5 100644
--- a/includes/command.inc
+++ b/includes/command.inc
@@ -418,20 +418,31 @@ function _drush_load_command_engines($command) {
     if (!in_array($engine, $engines)) {
       return drush_set_error('DRUSH_UNKNOWN_ENGINE_TYPE', dt('Unknown !engine_type engine !engine', array('!engine' => $engine, '!engine_type' => $engine_type)));
     }
-    $result = drush_include_engine($engine_type, $engine);
+
+    // Parameters to instantiate the engine.
+    $parameters = array();
+    foreach (array_keys($command['engines'][$engine_type]['parameters']) as $param) {
+      if (isset($command['engines'][$engine_type][$param])) {
+        $parameters[$param] = $command['engines'][$engine_type][$param];
+      }
+    }
+
+    // Include the engine and validate it.
+    $result = drush_include_engine($engine_type, $engine, NULL, NULL, $parameters);
     if ($result === FALSE) {
       return FALSE;
     }
-    elseif (is_object($result) && method_exists($result, 'validate')) {
-      $result = $result->validate();
+    if (is_object($result)) {
+      $valid = method_exists($result, 'validate')?$result->validate():TRUE;
+      if ($valid) {
+        drush_set_context($engine_type . '_engine', $result);
+      }
     }
     else {
       $function = strtr($engine_type, '-', '_') . '_validate';
-      if (function_exists($function)) {
-        $result = call_user_func($function);
-      }
+      $valid = function_exists($function)?call_user_func($function):TRUE;
     }
-    if (!$result) {
+    if (!$valid) {
       return FALSE;
     }
   }
@@ -458,7 +469,7 @@ function _drush_merge_engine_data(&$command) {
     // Override engine_info with customizations in the command.
     $config = $command['engines'][$engine_type] += $engine_info['info'];
 
-    // Add engine type global options to the command.
+    // Add engine_type global options to the command.
     $command['options'] += $config['options'];
 
     $engine_data = array();
@@ -492,7 +503,7 @@ function _drush_merge_engine_data(&$command) {
 
       // Extend the default engine description.
       $desc = $engine_info['engines'][$default]['description'];
-      $engine_info['engines'][$default]['description'] = dt('Default type engine.', array('type' => $engine_type)) . ' ' . $desc;
+      $engine_info['engines'][$default]['description'] = dt('Default !type engine.', array('!type' => $engine_type)) . ' ' . $desc;
 
       $engine_data += array(
         'options' => array(),
diff --git a/includes/drush.inc b/includes/drush.inc
index 5be9125..05c694f 100644
--- a/includes/drush.inc
+++ b/includes/drush.inc
@@ -89,6 +89,7 @@ function drush_get_engine_types_info() {
       'option' => FALSE,
       'default' => NULL,
       'options' => array(),
+      'parameters' => array(),
       'add-options-to-command' => FALSE,
     );
   }
@@ -183,7 +184,7 @@ function drush_get_engines($engine_type) {
  *   hook_drush_engine_$type item path.
  * @return TRUE or instanced object of available class on success. FALSE on fail.
  */
-function drush_include_engine($type, $engine, $version = NULL, $path = NULL) {
+function drush_include_engine($type, $engine, $version = NULL, $path = NULL, $parameters = array()) {
   $engine_info = drush_get_engines($type);
   if (!$path && isset($engine_info['engines'][$engine])) {
     $path = $engine_info['engines'][$engine]['path'];
@@ -194,7 +195,10 @@ function drush_include_engine($type, $engine, $version = NULL, $path = NULL) {
   if (drush_include($path, $engine, $version)) {
     $class = 'drush_' . $type . '_' . $engine;
     if (class_exists($class)) {
-      return new $class();
+      $instance = new $class($parameters);
+      $instance->engine_type = $type;
+      $instance->engine = $engine;
+      return $instance;
     }
     return TRUE;
   }
diff --git a/includes/engines.inc b/includes/engines.inc
new file mode 100644
index 0000000..eab2016
--- /dev/null
+++ b/includes/engines.inc
@@ -0,0 +1,28 @@
+<?php
+
+/**
+ * @file
+ * The drush engines API implementation and helpers.
+ */
+
+/**
+ * Interface for parametrized engines.
+ */
+interface drush_parametrized_engine {
+  function __construct($parameters);
+}
+
+/**
+ * Invoke drush_ENGINE_TYPE_help_alter for each engine that implements it.
+ *
+ * @see drush_command_invoke_all_ref(), drush_command_invoke_all()
+ */
+function drush_engine_type_help_alter(&$command) {
+  $engine_types = drush_get_engine_types_info();
+  foreach (array_keys($engine_types) as $engine) {
+    $function = 'drush_' . $engine . '_help_alter';
+    if (function_exists($function)) {
+      $function($command);
+    }
+  }
+}
-- 
1.7.2.5


From ed3e0aca2e11bb82c71c28ca2c81ce0e2ebf20cc Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Jonathan=20Ara=C3=B1a=20Cruz?= <jonhattan@faita.net>
Date: Sun, 20 May 2012 18:59:23 +0200
Subject: [PATCH 2/3] Issue #1364808: Printout engine

---
 commands/core/core.drush.inc     |   70 ++++++++++++++++++++++++++++++++++++--
 commands/core/printout/json.inc  |   24 +++++++++++++
 commands/core/printout/pipe.inc  |   30 ++++++++++++++++
 commands/core/printout/table.inc |   43 +++++++++++++++++++++++
 commands/core/printout/yaml.inc  |   47 +++++++++++++++++++++++++
 5 files changed, 211 insertions(+), 3 deletions(-)
 create mode 100644 commands/core/printout/json.inc
 create mode 100644 commands/core/printout/pipe.inc
 create mode 100644 commands/core/printout/table.inc
 create mode 100644 commands/core/printout/yaml.inc

diff --git a/commands/core/core.drush.inc b/commands/core/core.drush.inc
index 02c2868..8545fce 100644
--- a/commands/core/core.drush.inc
+++ b/commands/core/core.drush.inc
@@ -341,15 +341,33 @@ function core_core_rsync_complete() {
  */
 
 /**
- * Implementation of hook_drush_engine_type_info().
+ * Implements of hook_drush_engine_type_info().
  */
 function core_drush_engine_type_info() {
-  return array(
-    'drupal' => array(
+  $info = array();
+  $info['drupal'] = array();
+  $info['printout'] = array(
+    'description' => 'Output format.',
+    'option' => 'format',
+    'options' => array(
+      'fields' => array(
+        'description' => 'Fields to output.',
+        'example-value' => 'field1,field2',
+        'value' => 'optional',
+      ),
+    ),
+    'parameters' => array(
+      'fields_all' => 'All fields available for command output.',
+      'fields_name' => 'Associative array to optionally give a human name to any field.',
+      'fields_default' => 'Default fields used by the command.',
     ),
   );
+  return $info;
 }
 
+/**
+ * Implements hook_drush_engine_ENGINE_TYPE().
+ */
 function core_drush_engine_drupal() {
   $engines = array();
   $engines['batch'] = array();
@@ -360,6 +378,52 @@ function core_drush_engine_drupal() {
 }
 
 /**
+ * Implements hook_drush_engine_ENGINE_TYPE().
+ */
+function core_drush_engine_printout() {
+  $engines = array();
+  $engines['json'] = array(
+    'description' => 'Javascript Object Notation.',
+  );
+  $engines['pipe'] = array(
+    'description' => 'Pipeline format for scripting.',
+    'options' => array(
+      'delimiter' => array(
+        'description' => 'Delimiter for items in the output. Example: item1,item2,item3',
+        'example-value' => ',',
+      ),
+      'separator' => array(
+        'description' => 'Separator for a composed items output. Example foo1-bar,foo2-baz,foo3-qux',
+        'example-value' => '-',
+      ),
+    ),
+  );
+  $engines['table'] = array(
+    'description' => 'Tabular format.',
+  );
+  $engines['yaml'] = array(
+    'description' => 'YAML markup.',
+  );
+  return $engines;
+}
+
+/**
+ * Implements drush_ENGINE_TYPE_help_alter().
+ */
+function drush_printout_help_alter(&$command) {
+  $printout = $command['engines']['printout'];
+  $command['options']['fields']['example-value'] = implode(', ', $printout['fields_default']);
+  $command['options']['fields']['description'] .= ' '. dt('All available fields are: !fields.', array('!fields' => implode(', ', $printout['fields_all'])));
+}
+
+/**
+ * Interface for output format engines.
+ */
+interface drush_printout extends drush_parametrized_engine {
+  function dump($data);
+}
+
+/**
  * @} End of "Engine types".
  */
 
diff --git a/commands/core/printout/json.inc b/commands/core/printout/json.inc
new file mode 100644
index 0000000..ea2f39f
--- /dev/null
+++ b/commands/core/printout/json.inc
@@ -0,0 +1,24 @@
+<?php
+
+class drush_printout_json implements drush_printout {
+  function __construct($parameters) {
+    $fields = _convert_csv_to_array(drush_get_option('fields', ''));
+    if (empty($fields)) {
+      $fields = $parameters['fields_all'];
+    }
+    $this->fields = $fields;
+  }
+
+  function dump($items) {
+    $rows = array();
+    foreach ($items as $item) {
+      $row = array();
+      foreach ($this->fields as $field) {
+        $row[] = $item[$field];
+      }
+      $rows[] = $row;
+    }
+
+    drush_print(drush_json_encode($rows));
+  }
+}
diff --git a/commands/core/printout/pipe.inc b/commands/core/printout/pipe.inc
new file mode 100644
index 0000000..7b58ac5
--- /dev/null
+++ b/commands/core/printout/pipe.inc
@@ -0,0 +1,30 @@
+<?php
+
+class drush_printout_pipe implements drush_printout {
+  function __construct($parameters) {
+    // If user doesn't provide fields, just use the first of defaults one.
+    $fields = _convert_csv_to_array(drush_get_option('fields', ''));
+    if (empty($fields)) {
+      $fields = array($parameters['fields_default'][0]);
+    }
+    $this->fields = $fields;
+
+    $this->delimiter = drush_get_option('delimiter', ',');
+    $this->separator = drush_get_option('separator', '-');
+  }
+
+  function dump($items) {
+    $data = array();
+    foreach ($items as $item) {
+      $line = array();
+      foreach ($this->fields as $field) {
+        $line[] = $item[$field];
+      }
+      $line = implode($this->separator, $line);
+      $data[] = $line;
+    }
+
+    $data = implode($this->delimiter, $data);
+    drush_print_r($data);
+  }
+}
diff --git a/commands/core/printout/table.inc b/commands/core/printout/table.inc
new file mode 100644
index 0000000..0dcc1e3
--- /dev/null
+++ b/commands/core/printout/table.inc
@@ -0,0 +1,43 @@
+<?php
+
+class drush_printout_table implements drush_printout {
+  function __construct($parameters) {
+    $fields = _convert_csv_to_array(drush_get_option('fields', ''));
+    if (empty($fields)) {
+      $fields = $parameters['fields_default'];
+    }
+    $this->fields = $fields;
+
+    $header = array();
+    foreach ($fields as $field) {
+      if (isset($parameters['fields_name'][$field])) {
+        $header[] = $parameters['fields_name'][$field];
+      }
+      else {
+        $header[] = ucfirst($field);
+      }
+    }
+    $this->header = $header;
+  }
+
+  function dump($items) {
+    $rows = array($this->header);
+    foreach ($items as $item) {
+      $row = array();
+      foreach ($this->fields as $field) {
+        $data = $item[$field];
+        if (is_array($data)) {
+          $data = implode(', ', $data);
+        }
+        $row[] = $data;
+      }
+      $rows[] = $row;
+    }
+
+    if (isset($this->caption)) {
+      drush_print($this->caption);
+    }
+
+    drush_print_table($rows, TRUE);
+  }
+}
diff --git a/commands/core/printout/yaml.inc b/commands/core/printout/yaml.inc
new file mode 100644
index 0000000..897926a
--- /dev/null
+++ b/commands/core/printout/yaml.inc
@@ -0,0 +1,47 @@
+<?php
+
+class drush_printout_yaml implements drush_printout {
+  function validate() {
+    if (!class_exists('sfYamlDumper')) {
+      return drush_set_error('DRUSH_PRINTOUT_YAML_NOT_FOUND', dt("YAML not found."));
+    }
+    return TRUE;
+  }
+
+  function __construct($parameters) {
+    $fields = _convert_csv_to_array(drush_get_option('fields', ''));
+    if (empty($fields)) {
+      $this->all_fields = TRUE;
+    }
+    else {
+      $this->all_fields = FALSE;
+      $this->fields = $fields;
+    }
+  }
+
+  function getDumper() {
+    static $dumper = FALSE;
+    if (!$dumper) {
+      $dumper = new sfYamlDumper();
+    }
+    return $dumper;
+  }
+
+  function dump($items) {
+    if ($this->all_fields) {
+      $rows = $items;
+    }
+    else {
+      $rows = array();
+      foreach ($items as $item) {
+        $row = array();
+        foreach ($this->fields as $field) {
+          $row[] = $item[$field];
+        }
+        $rows[] = $row;
+      }
+    }
+
+    drush_print($this->getDumper()->dump($rows));
+  }
+}
-- 
1.7.2.5


From 7f559a590899560124b10756bc687f78c84570c6 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Jonathan=20Ara=C3=B1a=20Cruz?= <jonhattan@faita.net>
Date: Sun, 20 May 2012 19:00:02 +0200
Subject: [PATCH 3/3] Change pm-releases to use printout engine

---
 commands/pm/pm.drush.inc |   25 ++++++++++++++-----------
 1 files changed, 14 insertions(+), 11 deletions(-)

diff --git a/commands/pm/pm.drush.inc b/commands/pm/pm.drush.inc
index ff6ef13..1db06c1 100644
--- a/commands/pm/pm.drush.inc
+++ b/commands/pm/pm.drush.inc
@@ -149,7 +149,6 @@ function pm_drush_command() {
       'package' => 'Filter by project packages. You can use multiple comma separated values. (i.e. --package="Core - required,Other").',
       'core' => 'Filter out extensions that are not in drupal core.',
       'no-core' => 'Filter out extensions that are provided by drupal core.',
-      'pipe' => 'Returns a whitespace delimited list of the names of the resulting extensions.',
     ),
     'aliases' => array('pml'),
   );
@@ -223,6 +222,12 @@ function pm_drush_command() {
     'bootstrap' => DRUSH_BOOTSTRAP_MAX,
     'engines' => array(
       'release_info',
+      'printout' => array(
+        'default' => 'table',
+        'fields_all' => array('name', 'version', 'tag', 'version_major', 'version_extra', 'status', 'release_link', 'download_link', 'date', 'mdhash', 'filesize', 'release_date', 'release_status'),
+        'fields_name' => array('version' => 'Release', 'release_date' => 'Date', 'release_status' => 'Status'),
+        'fields_default' => array('version', 'release_date', 'release_status'),
+      ),
     ),
   );
   $items['pm-download'] = array(
@@ -1119,6 +1124,8 @@ function drush_pm_uninstall() {
  * Command callback. Show available releases for given project(s).
  */
 function drush_pm_releases() {
+  $printout = drush_get_context('printout_engine');
+
   if (!$requests = pm_parse_arguments(func_get_args(), FALSE)) {
     $requests = array('drupal');
   }
@@ -1134,19 +1141,15 @@ function drush_pm_releases() {
   $all = drush_get_option('all', FALSE);
   $dev = drush_get_option('dev', FALSE);
   foreach ($info as $name => $project) {
-    $header = dt('------- RELEASES FOR \'!name\' PROJECT -------', array('!name' => strtoupper($name)));
     $rows = array();
-    $rows[] = array(dt('Release'), dt('Date'), dt('Status'));
     $releases = release_info_filter_releases($project['releases'], $all, $dev);
-    foreach ($releases as $release) {
-      $rows[] = array(
-        $release['version'],
-        gmdate('Y-M-d', $release['date']),
-        implode(', ', $release['release_status'])
-      );
+    foreach ($releases as $key => $release) {
+      $releases[$key]['release_date'] = gmdate('Y-M-d', $release['date']);
+    }
+    if ($printout->engine == 'table') {
+      $printout->caption = dt('------- RELEASES FOR \'!name\' PROJECT -------', array('!name' => strtoupper($name)));
     }
-    drush_print($header);
-    drush_print_table($rows, TRUE, array(0 => 14));
+    $printout->dump($releases);
   }
 
   return $info;
-- 
1.7.2.5

