2
votes

Décorez l'assistant d'un moteur Rails pour inclure une préoccupation de l'application principale

Je voudrais inclure un ActiveSupport :: Concern d'une application dans un moteur, en décorant l'un des modules d'aide du moteur. Voici le Helper du moteur:

module MyEngine
  module MyHelper
    include MyConcern

    def do_stuff
    end
  end
end

Voici le souci de l'application principale:

module MyConcern
  extend ActiveSupport::Concern

  def do_this
  end

  def do_that
  end
end

Ci-dessous se trouve l'assistant de moteur décoré qui doit inclure le problème (à utiliser dans les vues du moteur) - il est déclaré dans l ' application principale , en utilisant le modèle de décorateur décrit dans le Guides de rails :

module MyEngine
  module MyHelper
  end
end

L'assistant décoré est correctement chargé par le engine, mais les vues du moteur ne peuvent appeler que "do_stuff". Les méthodes de MyConcern ne sont pas disponibles et j'ai du mal à comprendre pourquoi. J'ai également essayé d'inclure le problème en l'incorporant dans un appel MyEngine :: MyHelper.module_eval, mais cela n'a pas fonctionné non plus.

Est-ce que quelqu'un a déjà rencontré ce genre de problème? Est-ce que je prends cela dans le mauvais sens?


5 commentaires

ma réponse correspond-elle à vos exigences? sinon, veuillez compléter votre question


"Est-ce que je prends ça dans le mauvais sens?" - oui très probablement. L'idée est qu'un moteur doit encapsuler une fonctionnalité réutilisable. Bien qu'un moteur puisse s'appuyer sur l'application parente pour l'implémentation réelle (comme Devise par exemple), il ne devrait généralement pas avoir de dépendance stricte sur l'application parente, car cela rendrait très difficile de tester le moteur isolément ou de le réutiliser. Cela rend tout le processus d'extraction de la fonctionnalité inutile.


@max merci pour les commentaires, je suis d'accord qu'un moteur ne devrait pas avoir de dépendances sur l'application hôte. C'est pourquoi j'ai choisi de décorer l'assistant du côté de l'application principale, ce qui n'est pas une "dépendance dure" je crois, mais plutôt une injection de dépendance fournie par l'application principale pour personnaliser l'assistant du moteur. Le but ici est d'utiliser les méthodes Concern do_this et do_that dans la vue du moteur, car il partage un partiel commun avec l'application principale (généralement un volet de navigation supérieur avec des liens).


Donc, fondamentalement, ce que vous voulez vraiment faire est de déclarer un module dans votre application principale qui étend le module du moteur et non l'inverse. Notez que vous souhaitez utiliser extend et ne pas inclure lorsque vous ajoutez des méthodes à un module.


@max J'ai édité la question pour clarifier car je me rends compte que mon utilisation du modèle de décorateur n'était pas claire. J'ai déclaré l'assistant décoré du côté de l'application principale, et les méthodes déclarées directement sont disponibles dans le moteur. Ce qui ne fonctionne pas pour moi, c'est lorsque j'essaye d'inclure un module ActiveSupport :: Concern (également de l'application principale) dans cet assistant décoré.


3 Réponses :


0
votes

J'ai remplacé MyHelper du module à la classe

MyEngine::MyHelper.new.do_stuff
MyEngine::MyHelper.new.do_this
MyEngine::MyHelper.new.do_that

Lors de l'appel:

module MyConcern
  extend ActiveSupport::Concern

  def do_this
    "do this"
  end

  def do_that
    "do that"
  end
end

module MyEngine
  class MyHelper
    include ::MyConcern

    def do_stuff
      "do stuff"
    end
  end
end

Le résultat sera:

faire des choses

faites ceci

fais ça


1 commentaires

Salut @Xero, merci pour votre réponse. Malheureusement, l'utilisation d'une classe n'est pas une option ici, car Rails nécessite que les helpers soient déclarés en tant que modules afin de les inclure dans le contexte des vues.



0
votes

Je pense que vous raisonnez peut-être à ce sujet à l'envers.

Si votre moteur fournit:

module MainApp
  module MyHelper
    extend ::MyEngine::MyHelper
    def foo
      super
      do_something_else
    end
  end
end

Vous pouvez étendre la méthode (vous pouvez l'appeler decorate mais i ' Je ne sais pas si c'est techniquement le modèle de décorateur) dans votre application principale:

module MyEngine
  module MyHelper
    def foo
    end
  end
end

Lorsque vous utilisez le modèle module-mixin (ce que fait ActiveSupport :: Concern), vous étendez modules avec des modules et inclure des modules dans les classes.

Si votre moteur et votre application principale "partagent une partie" - il doit simplement être placé dans le moteur car Rails cherchera d'abord la vue lors du rendu dans le répertoire app / views de l'application avant de la rechercher les moteurs montés.

L'application principale peut donc toujours remplacer la fonctionnalité fournie par le moteur alors que l'inverse n'est pas vrai.

Si vous voulez rendre configurable une méthode fournie par un moteur, une meilleure idée est pour utiliser un paramètre de configuration Rails (ou une configuration de module séparée) ou simplement des arguments de méthode plutôt qu'un cirque de dépendance circulaire fou.


1 commentaires

Merci pour l'explication détaillée. Cela devient plus clair, mais ce n'est pas tout à fait le même cas d'utilisation: mon module ActiveSupport :: Concern fournit des méthodes pour générer des liens de navigation. Ces liens sont rendus dans le "top nav partial", à la fois dans l'application principale et dans les vues du moteur (les vues savent quel partiel utiliser avec un paramètre de configuration, comme suggéré). Lorsque j'étends l'assistant de moteur et que j'y ajoute explicitement des méthodes, cela fonctionne. Cependant, si j'essaie d'inclure un autre module (les liens de navigation déjà déclarés dans une préoccupation), les méthodes ne sont pas disponibles pour le moteur.



0
votes

J'ai donc finalement trouvé un moyen de faire cela de manière "propre", comme mentionné dans le commentaire suivant: https://groups.google.com/g/rubyonrails-core/c/PaABJDXnxyo/m/k-QUJEi9a9wJ

L'idée est de ajoutez un assistant d'espace réservé vide dans le moteur, par exemple:

module MyEngine
  module ExtendableHelper
    extend OtherGemFromMainApp::UsefulHelper
    include MainAppConcern
    
    def other_useful_method
      ...
    end
  end
end

Et puis remplacez-le dans l'application principale, en ajoutant des méthodes ou en incluant des préoccupations, des helpers d'autres gemmes, etc. ..

module MyEngine
  module ExtendableHelper
  end
end

Ainsi, l'aide utilisée par le moteur est celle fournie par l'application, et les méthodes UsefulHelper peuvent être appelées dans les vues du moteur .


0 commentaires