From bb0db591b251915e34cf605a42856a63fa802226 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Tobias=20St=C3=B6ckler?= <tstoeckler@gmail.com>
Date: Fri, 16 Oct 2015 14:31:22 +0200
Subject: [PATCH] Make it possible for asset libraries to return more than one
 core library

---
 libraries.module                              |  8 +-
 src/ExternalLibrary/Asset/AssetLibrary.php    |  2 +-
 .../Asset/AssetLibraryInterface.php           | 17 ++---
 ...yTrait.php => SingleAssetLibraryTrait.php} | 74 +++++++++++--------
 .../InvalidLibraryDependencyException.php     | 49 ++++++++++++
 .../LibraryClassNotFoundException.php         |  3 +-
 .../LibraryDefinitionNotFoundException.php    |  3 +-
 .../Utility/DependencyAccessorTrait.php       | 32 ++++++++
 .../Utility/LibraryAccessorTrait.php          | 32 ++++++++
 .../LibraryIdAccessorTrait.php}               |  8 +-
 10 files changed, 175 insertions(+), 53 deletions(-)
 rename src/ExternalLibrary/Asset/{AssetLibraryTrait.php => SingleAssetLibraryTrait.php} (54%)
 create mode 100644 src/ExternalLibrary/Exception/InvalidLibraryDependencyException.php
 create mode 100644 src/ExternalLibrary/Utility/DependencyAccessorTrait.php
 create mode 100644 src/ExternalLibrary/Utility/LibraryAccessorTrait.php
 rename src/ExternalLibrary/{Exception/LibraryExceptionTrait.php => Utility/LibraryIdAccessorTrait.php} (62%)

diff --git a/libraries.module b/libraries.module
index 4241a92..4e5cc39 100644
--- a/libraries.module
+++ b/libraries.module
@@ -19,13 +19,7 @@ function libraries_library_info_build() {
   $core_libraries = [];
   foreach ($library_manager->getRequiredLibraries() as $external_library) {
     if ($external_library instanceof AssetLibraryInterface) {
-      // Do not use AssetLibraryInterface::getAttachableAssetLibraryId() here
-      // because the module name is automatically prepended.
-      //
-      // The following means that the library namespace of this module is
-      // completely exhausted by the external library space. It should never
-      // provide any core asset libraries of its own.
-      $core_libraries[$external_library->getId()] = $external_library->toAttachableAssetLibrary();
+      $core_libraries += $external_library->getAttachableAssetLibraries();
     }
   }
   return $core_libraries;
diff --git a/src/ExternalLibrary/Asset/AssetLibrary.php b/src/ExternalLibrary/Asset/AssetLibrary.php
index 5cbfc20..55bab67 100644
--- a/src/ExternalLibrary/Asset/AssetLibrary.php
+++ b/src/ExternalLibrary/Asset/AssetLibrary.php
@@ -15,7 +15,7 @@ use Drupal\libraries\ExternalLibrary\ExternalLibraryTrait;
 class AssetLibrary implements AssetLibraryInterface {
 
   use ExternalLibraryTrait;
-  use AssetLibraryTrait;
+  use SingleAssetLibraryTrait;
 
   /**
    * {@inheritdoc}
diff --git a/src/ExternalLibrary/Asset/AssetLibraryInterface.php b/src/ExternalLibrary/Asset/AssetLibraryInterface.php
index dc37651..97aac92 100644
--- a/src/ExternalLibrary/Asset/AssetLibraryInterface.php
+++ b/src/ExternalLibrary/Asset/AssetLibraryInterface.php
@@ -18,22 +18,19 @@ use Drupal\libraries\ExternalLibrary\ExternalLibraryInterface;
  */
 interface AssetLibraryInterface extends ExternalLibraryInterface {
 
-  /**
-   * Gets the ID of the respective core asset library.
-   *
-   * @return string
-   *
-   * @todo Reconsider this method.
-   */
-  public function getAttachableAssetLibraryId();
-
   /**
    * Returns a core asset library array structure for this library.
    *
    * @return array
    *
+   * @see libraries_library_info_build()
+   * @see \Drupal\libraries\ExternalLibrary\Asset\SingleAssetLibraryTrait
+   *
+   * @throws \Drupal\libraries\ExternalLibrary\Exception\InvalidLibraryDependencyException
+   * @throws \LogicException
+   *
    * @todo Document the return value.
    */
-  public function toAttachableAssetLibrary();
+  public function getAttachableAssetLibraries();
 
 }
diff --git a/src/ExternalLibrary/Asset/AssetLibraryTrait.php b/src/ExternalLibrary/Asset/SingleAssetLibraryTrait.php
similarity index 54%
rename from src/ExternalLibrary/Asset/AssetLibraryTrait.php
rename to src/ExternalLibrary/Asset/SingleAssetLibraryTrait.php
index c73d3cb..9bc44ef 100644
--- a/src/ExternalLibrary/Asset/AssetLibraryTrait.php
+++ b/src/ExternalLibrary/Asset/SingleAssetLibraryTrait.php
@@ -7,51 +7,69 @@
 
 namespace Drupal\libraries\ExternalLibrary\Asset;
 
+use Drupal\libraries\ExternalLibrary\Exception\InvalidLibraryDependencyException;
 use Drupal\libraries\ExternalLibrary\ExternalLibraryInterface;
 
 /**
- * Provides a trait for asset libraries.
+ * Provides a trait for external libraries that contain a single asset library.
  *
  * @see \Drupal\libraries\ExternalLibrary\Asset\AssetLibraryInterface
  * @see \Drupal\libraries\ExternalLibrary\ExternalLibraryInterface
  */
-trait AssetLibraryTrait {
-
-  /**
-   * Gets the ID of the respective core asset library.
-   *
-   * @return string
-   *
-   * @see \Drupal\libraries\ExternalLibrary\Asset\AssetLibraryInterface::getAttachableAssetLibraryId()
-   */
-  public function getAttachableAssetLibraryId() {
-    return 'libraries/' . $this->getId();
-  }
+trait SingleAssetLibraryTrait {
 
   /**
    * Returns a core library array structure for this library.
    *
    * @return array
    *
-   * @see \Drupal\libraries\ExternalLibrary\Asset\AssetLibraryInterface::toAttachableAssetLibrary()
+   * @see \Drupal\libraries\ExternalLibrary\Asset\getAttachableAssetLibraries::getAttachableAssetLibraries()
+   *
+   * @throws \Drupal\libraries\ExternalLibrary\Exception\InvalidLibraryDependencyException
+   * @throws \LogicException
    *
    * @todo Document the return value.
    */
-  public function toAttachableAssetLibrary() {
-    $dependencies = array_map(function (ExternalLibraryInterface $dependency) {
-      // Asset libraries depending on PHP file libraries, for example, are not
-      // compatible with Drupal's render pipeline.
-      // @todo Consider doing something other than an assertion.
-      assert('$dependency instanceof \Drupal\libraries\ExternalLibrary\Asset\AssetLibraryInterface');
-      /** @var \Drupal\libraries\ExternalLibrary\Asset\AssetLibraryInterface $dependency */
-      return $dependency->getAttachableAssetLibraryId();
-    }, $this->getDependencies());
-    return [
+  public function getAttachableAssetLibraries() {
+    return [$this->getId() => [
       'version' => $this->getVersion(),
       'css' => $this->getCssAssets(),
       'js' => $this->getJsAssets(),
-      'dependencies' => $dependencies,
-    ];
+      'dependencies' => $this->processDependencies($this->getDependencies()),
+    ]];
+  }
+
+  /**
+   * Processes a list of dependencies into a list of attachable library IDs.
+   *
+   * @param \Drupal\libraries\ExternalLibrary\ExternalLibraryInterface[] $dependencies
+   *   An list of external libraries.
+   *
+   * @return string[]
+   *   A list of attachable asset library IDs.
+   *
+   * @throws \Drupal\libraries\ExternalLibrary\Exception\InvalidLibraryDependencyException
+   * @throws \LogicException
+   */
+  protected function processDependencies(array $dependencies) {
+    $attachable_dependency_ids = [];
+    foreach ($dependencies as $dependency) {
+      if (!$dependency instanceof AssetLibraryInterface) {
+        if (!$this instanceof ExternalLibraryInterface) {
+          $trait = self::class;
+          $interface = ExternalLibraryInterface::class;
+          throw new \LogicException("$trait may only be used in classes implementing $interface");
+        }
+        /** @var \Drupal\libraries\ExternalLibrary\ExternalLibraryInterface $this */
+        throw new InvalidLibraryDependencyException($this, $dependency);
+      }
+
+      foreach (array_keys($dependency->getAttachableAssetLibraries()) as $attachable_dependency_id) {
+        // @todo It is not very elegant to hard-code the namespace here.
+        $attachable_dependency_ids[] = 'libraries/' . $attachable_dependency_id;
+      }
+    }
+    return $attachable_dependency_ids;
   }
 
   /**
@@ -97,7 +115,7 @@ trait AssetLibraryTrait {
    * @see https://smacss.com/
    *
    * @todo Expand documentation.
-   * @todo Consider moving this back to AssetLibraryInterface
+   * @todo Consider adding separate methods for the CSS categories.
    */
   abstract protected function getCssAssets();
 
@@ -110,9 +128,7 @@ trait AssetLibraryTrait {
    *   options.
    *
    * @todo Expand documentation.
-   * @todo Consider moving this back to AssetLibraryInterface
    */
   abstract protected function getJsAssets();
 
 }
-
diff --git a/src/ExternalLibrary/Exception/InvalidLibraryDependencyException.php b/src/ExternalLibrary/Exception/InvalidLibraryDependencyException.php
new file mode 100644
index 0000000..a3c3353
--- /dev/null
+++ b/src/ExternalLibrary/Exception/InvalidLibraryDependencyException.php
@@ -0,0 +1,49 @@
+<?php
+
+/**
+ * @file
+ * Contains \Drupal\libraries\ExternalLibrary\Exception\InvalidLibraryDependencyException.
+ */
+
+namespace Drupal\libraries\ExternalLibrary\Exception;
+use Drupal\libraries\ExternalLibrary\DependencyAccessorTrait;
+use Drupal\libraries\ExternalLibrary\ExternalLibraryInterface;
+use Drupal\libraries\ExternalLibrary\LibraryAccessorTrait;
+
+/**
+ *
+ */
+class InvalidLibraryDependencyException extends \UnexpectedValueException {
+
+  use LibraryAccessorTrait;
+  use DependencyAccessorTrait;
+
+  /**
+   * Constructs a library exception.
+   *
+   * @param \Drupal\libraries\ExternalLibrary\ExternalLibraryInterface $library
+   *   The library with the invalid dependency.
+   * @param \Drupal\libraries\ExternalLibrary\ExternalLibraryInterface $dependency
+   *   The dependency.
+   * @param string $message
+   *   (optional) The exception message.
+   * @param int $code
+   *   (optional) The error code.
+   * @param \Exception $previous
+   *   (optional) The previous exception.
+   */
+  public function __construct(
+    ExternalLibraryInterface $library,
+    ExternalLibraryInterface $dependency,
+    $message = '',
+    $code = 0,
+    \Exception $previous = NULL
+  ) {
+    $this->library = $library;
+    $this->dependency = $dependency;
+    $message = $message ?: "The library '{$this->library->getId()}' cannot depend on the library '{$this->dependency->getId()}'.";
+    parent::__construct($message, $code, $previous);
+  }
+
+}
+
diff --git a/src/ExternalLibrary/Exception/LibraryClassNotFoundException.php b/src/ExternalLibrary/Exception/LibraryClassNotFoundException.php
index 15f1b6e..aadfaef 100644
--- a/src/ExternalLibrary/Exception/LibraryClassNotFoundException.php
+++ b/src/ExternalLibrary/Exception/LibraryClassNotFoundException.php
@@ -6,6 +6,7 @@
  */
 
 namespace Drupal\libraries\ExternalLibrary\Exception;
+use Drupal\libraries\ExternalLibrary\LibraryIdAccessorTrait;
 use Exception;
 
 /**
@@ -13,7 +14,7 @@ use Exception;
  */
 class LibraryClassNotFoundException extends \RuntimeException {
 
-  use LibraryExceptionTrait;
+  use LibraryIdAccessorTrait;
 
   /**
    * Constructs a library exception.
diff --git a/src/ExternalLibrary/Exception/LibraryDefinitionNotFoundException.php b/src/ExternalLibrary/Exception/LibraryDefinitionNotFoundException.php
index 7970658..53ebfe0 100644
--- a/src/ExternalLibrary/Exception/LibraryDefinitionNotFoundException.php
+++ b/src/ExternalLibrary/Exception/LibraryDefinitionNotFoundException.php
@@ -6,6 +6,7 @@
  */
 
 namespace Drupal\libraries\ExternalLibrary\Exception;
+use Drupal\libraries\ExternalLibrary\LibraryIdAccessorTrait;
 use Exception;
 
 /**
@@ -13,7 +14,7 @@ use Exception;
  */
 class LibraryDefinitionNotFoundException extends \RuntimeException {
 
-  use LibraryExceptionTrait;
+  use LibraryIdAccessorTrait;
 
   /**
    * Constructs a library exception.
diff --git a/src/ExternalLibrary/Utility/DependencyAccessorTrait.php b/src/ExternalLibrary/Utility/DependencyAccessorTrait.php
new file mode 100644
index 0000000..a64dd47
--- /dev/null
+++ b/src/ExternalLibrary/Utility/DependencyAccessorTrait.php
@@ -0,0 +1,32 @@
+<?php
+
+/**
+ * @file
+ * Contains \Drupal\libraries\ExternalLibrary\Utility\DependencyAccessorTrait.
+ */
+
+namespace Drupal\libraries\ExternalLibrary;
+
+/**
+ * Provides a trait for classes giving access to a library dependency.
+ */
+trait DependencyAccessorTrait {
+
+  /**
+   * The dependency.
+   *
+   * @var \Drupal\libraries\ExternalLibrary\ExternalLibraryInterface
+   */
+  protected $dependency;
+
+  /**
+   * Returns the dependency.
+   *
+   * @return \Drupal\libraries\ExternalLibrary\ExternalLibraryInterface
+   *   The library.
+   */
+  public function getLibrary() {
+    return $this->dependency;
+  }
+
+}
diff --git a/src/ExternalLibrary/Utility/LibraryAccessorTrait.php b/src/ExternalLibrary/Utility/LibraryAccessorTrait.php
new file mode 100644
index 0000000..3678325
--- /dev/null
+++ b/src/ExternalLibrary/Utility/LibraryAccessorTrait.php
@@ -0,0 +1,32 @@
+<?php
+
+/**
+ * @file
+ * Contains \Drupal\libraries\ExternalLibrary\Utility\LibraryAccessorTrait.
+ */
+
+namespace Drupal\libraries\ExternalLibrary;
+
+/**
+ * Provides a trait for classes giving access to a library.
+ */
+trait LibraryAccessorTrait {
+
+  /**
+   * The library.
+   *
+   * @var \Drupal\libraries\ExternalLibrary\ExternalLibraryInterface
+   */
+  protected $library;
+
+  /**
+   * Returns the library.
+   *
+   * @return \Drupal\libraries\ExternalLibrary\ExternalLibraryInterface
+   *   The library.
+   */
+  public function getLibrary() {
+    return $this->library;
+  }
+
+}
diff --git a/src/ExternalLibrary/Exception/LibraryExceptionTrait.php b/src/ExternalLibrary/Utility/LibraryIdAccessorTrait.php
similarity index 62%
rename from src/ExternalLibrary/Exception/LibraryExceptionTrait.php
rename to src/ExternalLibrary/Utility/LibraryIdAccessorTrait.php
index 57eca28..24dcb2e 100644
--- a/src/ExternalLibrary/Exception/LibraryExceptionTrait.php
+++ b/src/ExternalLibrary/Utility/LibraryIdAccessorTrait.php
@@ -2,15 +2,15 @@
 
 /**
  * @file
- * Contains \Drupal\libraries\ExternalLibrary\Exception\LibraryExceptionTrait.
+ * Contains \Drupal\libraries\ExternalLibrary\Utility\LibraryIdAccessorTrait.
  */
 
-namespace Drupal\libraries\ExternalLibrary\Exception;
+namespace Drupal\libraries\ExternalLibrary;
 
 /**
- * Provides a trait for library-related exceptions.
+ * Provides a trait for classes giving access to a library ID.
  */
-trait LibraryExceptionTrait {
+trait LibraryIdAccessorTrait {
 
   /**
    * The library ID of the library that caused the exception.
-- 
GitLab