Commit 926327cc authored by sun's avatar sun

Issue #958162 by tstoeckler, sun, good_man: Allow to apply callbacks to libraries.

parent 1dc44ff8
Libraries 7.x-2.x, xxxx-xx-xx
-----------------------------
#958162 by tstoeckler, sun, good_man: Allow to apply callbacks to libraries.
#1125904 by tstoeckler, boombatower: Fix drush libraries-list.
#1050076 by tstoeckler: Re-utilize libraries_detect() and remove libraries_detect_library().
#466090 by tstoeckler: Add update function.
......
......@@ -102,6 +102,41 @@
* names and whose values are sets of files to load for the module, using
* the same notion as the top-level 'files' property. Each specified file
* should contain the path to the file relative to the module it belongs to.
* - callbacks: An associative array whose keys are callback groups and whose
* values are arrays of callbacks to apply to the library in that group.
* Each callback receives the following arguments:
* - $library: An array of library information belonging to the top-level
* library, a specific version, a specific variant or a specific variant
* of a specific version. Because library information such as the 'files'
* property (see above) can be declared in all these different locations
* of the library array, but a callback may have to act on all these
* different parts of the library, it is called recursively for each
* library with a certain part of the libraries array passed as $library
* each time.
* - $version: If the $library array belongs to a certain version (see
* above), a string containing the version. NULL, otherwise.
* - $variant: If the $library array belongs to a certain variant (see
* above), a string containing the variant name. NULL, otherwise.
* Valid callback groups are:
* - prepare: Callbacks registered in this group are applied as soon as the
* library information has been retrieved via hook_libraries_info() or
* info files.
* - detect: Callbacks registered in this group are applied as soon as
* library detection has been completed. At this point the library
* contains the version-specific information, if specified, and following
* additional information is available:
* - $library['installed']: A boolean indicating whether the library is
* installed or not.
* - $library['version']: If it could be detected, a string containing the
* version of the library.
* - $library['variants'][$variant]['installed']: For each specified
* variant, a boolean indicating whether the variant is installed or
* not.
* Note that in this group the 'versions' property is no longer available.
* - load: Callbacks registered in this group are applied directly
* before this library is loaded. At this point the library contains
* variant-specific information, if specified. Note that in this group the
* 'variants' property is no longer available.
* Additional top-level properties can be registered as needed.
*
* @see hook_library()
......@@ -213,6 +248,14 @@ function hook_libraries_info() {
'js' => array('ex_lib.inc'),
),
),
// Optionally register callbacks to apply to the library during different
// stages of its lifetime ('callback groups'). Here, a callback is
// registered in the 'detect' group.
'callbacks' => array(
'detect' => array(
'mymodule_example_detect_callback',
),
),
);
// A very simple library. No changing APIs (hence, no versions), no variants.
......
......@@ -151,6 +151,60 @@ function libraries_scan_info_files() {
return $files;
}
/**
* Invokes library callbacks.
*
* @param $group
* A string containing the group of callbacks that is to be applied. Should be
* either 'prepare', 'detect', or 'load'.
* @param $library
* An array of library information, passed by reference.
*/
function libraries_invoke($group, &$library) {
foreach ($library['callbacks'][$group] as $callback) {
libraries_traverse_library($library, $callback);
}
}
/**
* Helper function to apply a callback to all parts of a library.
*
* Because library declarations can include variants and versions, and those
* version declarations can in turn include variants, modifying e.g. the 'files'
* property everywhere it is declared can be quite cumbersome, in which case
* this helper function is useful.
*
* @param $library
* An array of library information, passed by reference.
* @param $callback
* A string containing the callback to apply to all parts of a library.
*/
function libraries_traverse_library(&$library, $callback) {
// Always apply the callback to the top-level library.
$callback($library, NULL, NULL);
// Apply the callback to versions.
if (isset($library['versions'])) {
foreach ($library['versions'] as $version_string => &$version) {
$callback($version, $version_string, NULL);
// Versions can include variants as well.
if (isset($version['variants'])) {
foreach ($version['variants'] as $version_variant_name => &$version_variant) {
$callback($version_variant, $version_string, $version_variant_name);
}
}
}
}
// Apply the callback to variants.
if (isset($library['variants'])) {
foreach ($library['variants'] as $variant_name => &$variant) {
$callback($variant, NULL, $variant_name);
}
}
}
/**
* Returns information about registered libraries.
*
......@@ -205,11 +259,21 @@ function libraries_info($name = NULL) {
'variants' => array(),
'versions' => array(),
'integration files' => array(),
'callbacks' => array(
'prepare' => array(),
'detect' => array(),
'load' => array(),
),
);
}
// Allow modules to alter the registered libraries.
drupal_alter('libraries_info', $libraries);
// Invoke callbacks in the 'prepare' group.
foreach ($libraries as &$properties) {
libraries_invoke('prepare', $properties);
}
}
if (isset($name)) {
......@@ -340,6 +404,10 @@ function libraries_detect($name) {
// If we end up here, the library should be usable.
$library['installed'] = TRUE;
// Invoke callbacks in the 'detect' group.
libraries_invoke('detect', $library);
return $library;
}
......@@ -392,6 +460,9 @@ function libraries_load($name, $variant = NULL) {
// If the library (variant) is installed, load it.
$library['loaded'] = FALSE;
if ($library['installed']) {
// Invoke callbacks in the 'load' group.
libraries_invoke('load', $library);
$library['loaded'] = libraries_load_files($library);
}
$loaded[$name] = $library;
......
......@@ -137,6 +137,81 @@ class LibrariesTestCase extends DrupalWebTestCase {
$this->verbose(var_export($library, TRUE));
$this->assertEqual($library['variants']['example_variant']['installed'], TRUE, 'Existing variant found.');
// Test the applying of callbacks.
$expected = array_merge(libraries_info('example_empty'), array(
'machine name' => 'example_callback',
'name' => 'Example callback',
'library path' => drupal_get_path('module', 'libraries') . '/tests/example',
'version' => '1',
'versions' => array(
'1' => array(
'variants' => array(
'example_variant' => array(
'prepare callback' => 'not applied',
'detect callback' => 'not applied',
'load callback' => 'not applied',
),
),
'prepare callback' => 'not applied',
'detect callback' => 'not applied',
'load callback' => 'not applied',
),
),
'variants' => array(
'example_variant' => array(
'prepare callback' => 'not applied',
'detect callback' => 'not applied',
'load callback' => 'not applied',
),
),
'callbacks' => array(
'prepare' => array('_libraries_test_prepare_callback'),
'detect' => array('_libraries_test_detect_callback'),
'load' => array('_libraries_test_load_callback'),
),
'prepare callback' => 'not applied',
'detect callback' => 'not applied',
'load callback' => 'not applied',
));
// Test a callback in the 'prepare' phase.
$expected['prepare callback'] = 'applied (top-level)';
$expected['versions']['1']['prepare callback'] = 'applied (version 1)';
$expected['versions']['1']['variants']['example_variant']['prepare callback'] = 'applied (version 1, variant example_variant)';
$expected['variants']['example_variant']['prepare callback'] = 'applied (variant example_variant)';
$library = libraries_info('example_callback');
$this->verbose(var_export($expected, TRUE));
$this->verbose(var_export($library, TRUE));
$this->assertEqual($library, $expected, 'Prepare callback was applied correctly.');
// Test a callback in the 'detect' phase.
unset($expected['versions']);
$expected['installed'] = TRUE;
$expected['prepare callback'] = 'applied (version 1)';
$expected['variants']['example_variant']['installed'] = TRUE;
$expected['variants']['example_variant']['prepare callback'] = 'applied (version 1, variant example_variant)';
$expected['detect callback'] = 'applied (top-level)';
$expected['variants']['example_variant']['detect callback'] = 'applied (variant example_variant)';
$library = libraries_detect('example_callback');
$this->verbose(var_export($expected, TRUE));
$this->verbose(var_export($library, TRUE));
$this->assertEqual($library, $expected, 'Detect callback was applied correctly.');
// Test a callback in the 'load' phase.
unset($expected['variants']);
$expected['loaded'] = 0;
$expected['load callback'] = 'applied (top-level)';
$library = libraries_load('example_callback');
$this->verbose(var_export($expected, TRUE));
$this->verbose(var_export($library, TRUE));
$this->assertEqual($library, $expected, 'Load callback was applied correctly.');
// This is not recommended usually and is only used for testing purposes.
drupal_static_reset('libraries_load');
$expected['prepare callback'] = 'applied (version 1, variant example_variant)';
$expected['detect callback'] = 'applied (variant example_variant)';
$library = libraries_load('example_callback', 'example_variant');
$this->verbose(var_export($expected, TRUE));
$this->verbose(var_export($library, TRUE));
$this->assertEqual($library, $expected, 'Load callback was applied correctly to a variant.');
// Test loading of a simple library with a top-level files property.
$this->drupalGet('libraries_test/files');
$this->assertLibraryFiles('example_1', 'File loading');
......
......@@ -200,6 +200,46 @@ function libraries_test_libraries_info() {
),
);
// Test the applying of callbacks.
$libraries['example_callback'] = array(
'name' => 'Example callback',
'library path' => drupal_get_path('module', 'libraries') . '/tests/example',
'version' => '1',
'versions' => array(
'1' => array(
'variants' => array(
'example_variant' => array(
// These keys are for testing purposes only.
'prepare callback' => 'not applied',
'detect callback' => 'not applied',
'load callback' => 'not applied',
),
),
// These keys are for testing purposes only.
'prepare callback' => 'not applied',
'detect callback' => 'not applied',
'load callback' => 'not applied',
),
),
'variants' => array(
'example_variant' => array(
// These keys are for testing purposes only.
'prepare callback' => 'not applied',
'detect callback' => 'not applied',
'load callback' => 'not applied',
),
),
'callbacks' => array(
'prepare' => array('_libraries_test_prepare_callback'),
'detect' => array('_libraries_test_detect_callback'),
'load' => array('_libraries_test_load_callback'),
),
// These keys are for testing purposes only.
'prepare callback' => 'not applied',
'detect callback' => 'not applied',
'load callback' => 'not applied',
);
// This library is used together with libraries_info() to be populated with
// the defaults.
$libraries['example_empty'] = array();
......@@ -285,6 +325,107 @@ function _libraries_test_return_installed($library, $name, $installed) {
return $installed;
}
/**
* Sets the 'prepare callback' key.
*
* This function is used as a test callback for the 'prepare' callback group.
*
* @param $library
* An array of library information, which may be version- or variant-specific.
* Passed by reference.
* @param $version
* The version the library information passed in $library belongs to, or NULL
* if the passed library information is not version-specific.
* @param $variant
* The variant the library information passed in $library belongs to, or NULL
* if the passed library information is not variant-specific.
*
* @see _libraries_test_callback()
*/
function _libraries_test_prepare_callback(&$library, $version, $variant) {
_libraries_test_callback($library, $version, $variant, 'prepare');
}
/**
* Sets the 'detect callback' key.
*
* This function is used as a test callback for the 'detect' callback group.
*
* @param $library
* An array of library information, which may be version- or variant-specific.
* Passed by reference.
* @param $version
* The version the library information passed in $library belongs to, or NULL
* if the passed library information is not version-specific.
* @param $variant
* The variant the library information passed in $library belongs to, or NULL
* if the passed library information is not variant-specific.
*
* @see _libraries_test_callback()
*/
function _libraries_test_detect_callback(&$library, $version, $variant) {
_libraries_test_callback($library, $version, $variant, 'detect');
}
/**
* Sets the 'load callback' key.
*
* This function is used as a test callback for the 'load' callback group.
*
* @param $library
* An array of library information, which may be version- or variant-specific.
* Passed by reference.
* @param $version
* The version the library information passed in $library belongs to, or NULL
* if the passed library information is not version-specific.
* @param $variant
* The variant the library information passed in $library belongs to, or NULL
* if the passed library information is not variant-specific.
*
* @see _libraries_test_callback()
*/
function _libraries_test_load_callback(&$library, $version, $variant) {
_libraries_test_callback($library, $version, $variant, 'load');
}
/**
* Sets the '[group] callback' key, where [group] is prepare, detect, or load.
*
* This function is used as a test callback for the all callback groups.
*
* It sets the '[group] callback' (see above) key to 'applied ([part])' where
* [part] is either 'top-level', 'version x.y' (where x.y is the passed-in
* version string), 'variant example' (where example is the passed-in variant
* name), or 'version x.y, variant example' (see above), depending on the part
* of the library the passed-in library information belongs to.
*
* @param $library
* An array of library information, which may be version- or variant-specific.
* Passed by reference.
* @param $version
* The version the library information passed in $library belongs to, or NULL
* if the passed library information is not version-specific.
* @param $variant
* The variant the library information passed in $library belongs to, or NULL
* if the passed library information is not variant-specific.
*/
function _libraries_test_callback(&$library, $version, $variant, $group) {
$string = 'applied';
if (isset($version) && isset($variant)) {
$string .= " (version $version, variant $variant)";
}
elseif (isset($version)) {
$string .= " (version $version)";
}
elseif (isset($variant)) {
$string .= " (variant $variant)";
}
else {
$string .= ' (top-level)';
}
$library["$group callback"] = $string;
}
/**
* Implements hook_menu().
*/
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment