diff --git a/libraries.api.php b/libraries.api.php index 66fa772..fa64b20 100644 --- a/libraries.api.php +++ b/libraries.api.php @@ -118,10 +118,14 @@ * - $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 + * - info: 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 + * - pre detect: Callbacks registered in this group are applied when a + * library is about to be detected, before the version callback is + * invoked. At this point following additional information is available: + * - $library['library path']: The path on the file system to the library. + * - post 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: diff --git a/libraries.module b/libraries.module index 9abbcad..412f8a4 100644 --- a/libraries.module +++ b/libraries.module @@ -157,7 +157,7 @@ function libraries_scan_info_files() { * * @param $group * A string containing the group of callbacks that is to be applied. Should be - * either 'prepare', 'detect', or 'load'. + * either 'info', 'pre detect', 'post detect', or 'load'. * @param $library * An array of library information, passed by reference. */ @@ -246,34 +246,15 @@ function libraries_info($name = NULL) { // Provide defaults. foreach ($libraries as $machine_name => &$properties) { - $properties += array( - 'machine name' => $machine_name, - 'name' => $machine_name, - 'vendor url' => '', - 'download url' => '', - 'path' => '', - 'library path' => NULL, - 'version callback' => 'libraries_get_version', - 'version arguments' => array(), - 'files' => array(), - 'variants' => array(), - 'versions' => array(), - 'integration files' => array(), - 'callbacks' => array(), - ); - $properties['callbacks'] += array( - 'prepare' => array(), - 'detect' => array(), - 'load' => array(), - ); + libraries_info_defaults($properties, $machine_name); } // Allow modules to alter the registered libraries. drupal_alter('libraries_info', $libraries); - // Invoke callbacks in the 'prepare' group. + // Invoke callbacks in the 'info' group. foreach ($libraries as &$properties) { - libraries_invoke('prepare', $properties); + libraries_invoke('info', $properties); } } @@ -284,6 +265,41 @@ function libraries_info($name = NULL) { } /** + * Applies default properties to a library definition. + * + * @param $library + * An array of library information. + * @param $machine_name + * The machine name of a library to return registered information + * for, or FALSE if no library with the given name exists. If omitted, + * information about all libraries is returned. + */ +function libraries_info_defaults(&$library, $machine_name) { + $library += array( + 'machine name' => $machine_name, + 'name' => $machine_name, + 'vendor url' => '', + 'download url' => '', + 'path' => '', + 'library path' => NULL, + 'version callback' => 'libraries_get_version', + 'version arguments' => array(), + 'files' => array(), + 'variants' => array(), + 'versions' => array(), + 'integration files' => array(), + 'callbacks' => array(), + ); + $library['callbacks'] += array( + 'info' => array(), + 'pre detect' => array(), + 'post detect' => array(), + 'load' => array(), + ); + return $library; +} + +/** * Tries to detect a library and its installed version. * * @todo We need to figure out whether, and if, how we want to retain the @@ -315,101 +331,113 @@ function libraries_info($name = NULL) { * @see libraries_info() */ function libraries_detect($name) { - $library = libraries_info($name); + $detected = &drupal_static(__FUNCTION__, array()); - $library['installed'] = FALSE; + if (!isset($detected[$name])) { + $library = libraries_info($name); - // Check whether the library exists. - if (!isset($library['library path'])) { - $library['library path'] = libraries_get_path($library['machine name']); - } - if (!file_exists($library['library path'])) { - $library['error'] = 'not found'; - $library['error message'] = t('The %library library could not be found.', array( - '%library' => $library['name'], - )); - return $library; - } + $library['installed'] = FALSE; - // Detect library version, if not hardcoded. - if (!isset($library['version'])) { - // We support both a single parameter, which is an associative array, and an - // indexed array of multiple parameters. - if (isset($library['version arguments'][0])) { - // Add the library as the first argument. - $library['version'] = call_user_func_array($library['version callback'], array_merge(array($library), $library['version arguments'])); - } - else { - $library['version'] = $library['version callback']($library, $library['version arguments']); + // Check whether the library exists. + if (!isset($library['library path'])) { + $library['library path'] = libraries_get_path($library['machine name']); } - if (empty($library['version'])) { - $library['error'] = 'not detected'; - $library['error message'] = t('The version of the %library library could not be detected.', array( + if (!file_exists($library['library path'])) { + $library['error'] = 'not found'; + $library['error message'] = t('The %library library could not be found.', array( '%library' => $library['name'], )); - return $library; + $detected[$name] = $library; + return $detected[$name]; } - } - // Determine to which supported version the installed version maps. - if (!empty($library['versions'])) { - ksort($library['versions']); - $version = 0; - foreach ($library['versions'] as $supported_version => $version_properties) { - if (version_compare($library['version'], $supported_version, '>=')) { - $version = $supported_version; + // Invoke callbacks in the 'pre detect' group. + libraries_invoke('pre detect', $library); + + // Detect library version, if not hardcoded. + if (!isset($library['version'])) { + // We support both a single parameter, which is an associative array, and an + // indexed array of multiple parameters. + if (isset($library['version arguments'][0])) { + // Add the library as the first argument. + $library['version'] = call_user_func_array($library['version callback'], array_merge(array($library), $library['version arguments'])); + } + else { + $library['version'] = $library['version callback']($library, $library['version arguments']); + } + if (empty($library['version'])) { + $library['error'] = 'not detected'; + $library['error message'] = t('The version of the %library library could not be detected.', array( + '%library' => $library['name'], + )); + $detected[$name] = $library; + return $detected[$name]; } } - if (!$version) { - $library['error'] = 'not supported'; - $library['error message'] = t('The installed version %version of the %library library is not supported.', array( - '%version' => $library['version'], - '%library' => $library['name'], - )); - return $library; - } - - // Apply version specific definitions and overrides. - $library = array_merge($library, $library['versions'][$version]); - unset($library['versions']); - } - // Check each variant if it is installed. - if (!empty($library['variants'])) { - foreach ($library['variants'] as $variant_name => &$variant) { - // If no variant callback has been set, assume the variant to be - // installed. - if (!isset($variant['variant callback'])) { - $variant['installed'] = TRUE; + // Determine to which supported version the installed version maps. + if (!empty($library['versions'])) { + ksort($library['versions']); + $version = 0; + foreach ($library['versions'] as $supported_version => $version_properties) { + if (version_compare($library['version'], $supported_version, '>=')) { + $version = $supported_version; + } } - else { - // We support both a single parameter, which is an associative array, - // and an indexed array of multiple parameters. - if (isset($variant['variant arguments'][0])) { - // Add the library as the first argument, and the variant name as the second. - $variant['installed'] = call_user_func_array($variant['variant callback'], array_merge(array($library, $variant_name), $variant['variant arguments'])); + if (!$version) { + $library['error'] = 'not supported'; + $library['error message'] = t('The installed version %version of the %library library is not supported.', array( + '%version' => $library['version'], + '%library' => $library['name'], + )); + $detected[$name] = $library; + return $detected[$name]; + } + + // Apply version specific definitions and overrides. + $library = array_merge($library, $library['versions'][$version]); + unset($library['versions']); + } + + // Check each variant if it is installed. + if (!empty($library['variants'])) { + foreach ($library['variants'] as $variant_name => &$variant) { + // If no variant callback has been set, assume the variant to be + // installed. + if (!isset($variant['variant callback'])) { + $variant['installed'] = TRUE; } else { - $variant['installed'] = $variant['variant callback']($library, $variant_name, $variant['variant arguments']); - } - if (!$variant['installed']) { - $variant['error'] = 'not found'; - $variant['error message'] = t('The %variant variant of the %library library could not be found.', array( - '%variant' => $variant_name, - '%library' => $library['name'], - )); + // We support both a single parameter, which is an associative array, + // and an indexed array of multiple parameters. + if (isset($variant['variant arguments'][0])) { + // Add the library as the first argument, and the variant name as the second. + $variant['installed'] = call_user_func_array($variant['variant callback'], array_merge(array($library, $variant_name), $variant['variant arguments'])); + } + else { + $variant['installed'] = $variant['variant callback']($library, $variant_name, $variant['variant arguments']); + } + if (!$variant['installed']) { + $variant['error'] = 'not found'; + $variant['error message'] = t('The %variant variant of the %library library could not be found.', array( + '%variant' => $variant_name, + '%library' => $library['name'], + )); + } } } } - } - // If we end up here, the library should be usable. - $library['installed'] = TRUE; + // If we end up here, the library should be usable. + $library['installed'] = TRUE; - // Invoke callbacks in the 'detect' group. - libraries_invoke('detect', $library); + // Invoke callbacks in the 'post detect' group. + libraries_invoke('post detect', $library); - return $library; + $detected[$name] = $library; + } + + return $detected[$name]; } /** diff --git a/tests/libraries.test b/tests/libraries.test index ada68b9..f80fa38 100644 --- a/tests/libraries.test +++ b/tests/libraries.test @@ -138,8 +138,7 @@ class LibrariesTestCase extends DrupalWebTestCase { $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', + $expected = array( 'name' => 'Example callback', 'library path' => drupal_get_path('module', 'libraries') . '/tests/example', 'version' => '1', @@ -147,69 +146,96 @@ class LibrariesTestCase extends DrupalWebTestCase { '1' => array( 'variants' => array( 'example_variant' => array( - 'prepare callback' => 'not applied', - 'detect callback' => 'not applied', + 'info callback' => 'not applied', + 'pre detect callback' => 'not applied', + 'post detect callback' => 'not applied', 'load callback' => 'not applied', ), ), - 'prepare callback' => 'not applied', - 'detect callback' => 'not applied', + 'info callback' => 'not applied', + 'pre detect callback' => 'not applied', + 'post detect callback' => 'not applied', 'load callback' => 'not applied', ), ), 'variants' => array( 'example_variant' => array( - 'prepare callback' => 'not applied', - 'detect callback' => 'not applied', + 'info callback' => 'not applied', + 'pre detect callback' => 'not applied', + 'post detect callback' => 'not applied', 'load callback' => 'not applied', ), ), 'callbacks' => array( - 'prepare' => array('_libraries_test_prepare_callback'), - 'detect' => array('_libraries_test_detect_callback'), + 'info' => array('_libraries_test_info_callback'), + 'pre detect' => array('_libraries_test_pre_detect_callback'), + 'post detect' => array('_libraries_test_post_detect_callback'), 'load' => array('_libraries_test_load_callback'), ), - 'prepare callback' => 'not applied', - 'detect callback' => 'not applied', + 'info callback' => 'not applied', + 'pre detect callback' => 'not applied', + 'post 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)'; + 'module' => 'libraries_test', + ); + libraries_info_defaults($expected, 'example_callback'); + + // Test a callback in the 'info' phase. + $expected['info callback'] = 'applied (top-level)'; + $expected['versions']['1']['info callback'] = 'applied (version 1)'; + $expected['versions']['1']['variants']['example_variant']['info callback'] = 'applied (version 1, variant example_variant)'; + $expected['variants']['example_variant']['info 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. + $this->verbose('Expected:
' . var_export($expected, TRUE) . '
'); + $this->verbose('Actual:
' . var_export($library, TRUE) . '
'); + $this->assertEqual($library, $expected, 'Info callback was applied correctly.'); + + // Test a callback in the 'pre detect' and 'post detect' phases. + // Successfully detected libraries should only contain version information + // for the detected version and thus, be marked as installed. unset($expected['versions']); $expected['installed'] = TRUE; - $expected['prepare callback'] = 'applied (version 1)'; + + // Additionally, version-specific properties of the detected version are + // supposed to override the corresponding top-level properties. + $expected['info callback'] = 'applied (version 1)'; + $expected['pre detect callback'] = 'applied (version 1)'; + $expected['post detect callback'] = 'applied (top-level)'; $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)'; + $expected['variants']['example_variant']['info callback'] = 'applied (version 1, variant example_variant)'; + $expected['variants']['example_variant']['pre detect callback'] = 'applied (version 1, variant example_variant)'; + $expected['variants']['example_variant']['post 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.'); + $this->verbose('Expected:
' . var_export($expected, TRUE) . '
'); + $this->verbose('Actual:
' . var_export($library, TRUE) . '
'); + $this->assertEqual($library, $expected, 'Pre and post detect callbacks were applied correctly.'); // Test a callback in the 'load' phase. + // Successfully loaded libraries should only contain information about the + // already loaded variant. 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->verbose('Expected:
' . var_export($expected, TRUE) . '
'); + $this->verbose('Actual:
' . 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)'; + + // Successfully loaded library variants are supposed to contain the specific + // variant information only. + $expected['info callback'] = 'applied (version 1, variant example_variant)'; + $expected['pre detect callback'] = 'applied (version 1, variant example_variant)'; + $expected['post 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->verbose('Expected:
' . var_export($expected, TRUE) . '
'); + $this->verbose('Actual:
' . 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. diff --git a/tests/libraries_test.module b/tests/libraries_test.module index bf1369f..545ab43 100644 --- a/tests/libraries_test.module +++ b/tests/libraries_test.module @@ -210,33 +210,38 @@ function libraries_test_libraries_info() { 'variants' => array( 'example_variant' => array( // These keys are for testing purposes only. - 'prepare callback' => 'not applied', - 'detect callback' => 'not applied', + 'info callback' => 'not applied', + 'pre detect callback' => 'not applied', + 'post detect callback' => 'not applied', 'load callback' => 'not applied', ), ), // These keys are for testing purposes only. - 'prepare callback' => 'not applied', - 'detect callback' => 'not applied', + 'info callback' => 'not applied', + 'pre detect callback' => 'not applied', + 'post 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', + 'info callback' => 'not applied', + 'pre detect callback' => 'not applied', + 'post detect callback' => 'not applied', 'load callback' => 'not applied', ), ), 'callbacks' => array( - 'prepare' => array('_libraries_test_prepare_callback'), - 'detect' => array('_libraries_test_detect_callback'), + 'info' => array('_libraries_test_info_callback'), + 'pre detect' => array('_libraries_test_pre_detect_callback'), + 'post detect' => array('_libraries_test_post_detect_callback'), 'load' => array('_libraries_test_load_callback'), ), // These keys are for testing purposes only. - 'prepare callback' => 'not applied', - 'detect callback' => 'not applied', + 'info callback' => 'not applied', + 'pre detect callback' => 'not applied', + 'post detect callback' => 'not applied', 'load callback' => 'not applied', ); @@ -326,61 +331,42 @@ function _libraries_test_return_installed($library, $name, $installed) { } /** - * Sets the 'prepare callback' key. + * Sets the 'info 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. + * This is used as a test callback for the 'info' callback group. * * @see _libraries_test_callback() */ -function _libraries_test_prepare_callback(&$library, $version, $variant) { - _libraries_test_callback($library, $version, $variant, 'prepare'); +function _libraries_test_info_callback(&$library, $version, $variant) { + _libraries_test_callback($library, $version, $variant, 'info'); } /** - * Sets the 'detect callback' key. + * Sets the 'pre detect callback' key. * - * This function is used as a test callback for the 'detect' callback group. + * This is used as a test callback for the 'pre 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_pre_detect_callback(&$library, $version, $variant) { + _libraries_test_callback($library, $version, $variant, 'pre detect'); +} + +/** + * Sets the 'post detect callback' key. + * + * This is used as a test callback for the 'post detect' callback group. * * @see _libraries_test_callback() */ -function _libraries_test_detect_callback(&$library, $version, $variant) { - _libraries_test_callback($library, $version, $variant, 'detect'); +function _libraries_test_post_detect_callback(&$library, $version, $variant) { + _libraries_test_callback($library, $version, $variant, 'post 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. + * This is used as a test callback for the 'load' callback group. * * @see _libraries_test_callback() */ @@ -389,7 +375,9 @@ function _libraries_test_load_callback(&$library, $version, $variant) { } /** - * Sets the '[group] callback' key, where [group] is prepare, detect, or load. + * Sets the '[group] callback' key. + * + * [group] can be info, pre detect, post detect, or load. * * This function is used as a test callback for the all callback groups. *