51 votes

Rails : Je ne peux pas appeler une fonction d'un module dans /lib - qu'est-ce que je fais mal ?

J'ai un module enregistré dans /lib sous le nom de test_functions.rb qui ressemble à ceci

module TestFunctions
  def abc
    puts 123
  end
end

En allant dans ruby script/runner, je peux voir que le module se charge automatiquement (bonne vieille convention sur la configuration et tout ça...).

>> TestFunctions.instance_methods
=> ["abc"]

donc la méthode est connue, essayons de l'appeler

>> TestFunctions.abc
NoMethodError: undefined method `abc' for TestFunctions:Module from (irb):3

Non. Et ça ?

>> TestFunctions::abc
NoMethodError: undefined method `abc' for TestFunctions:Module from (irb):4

Test Encore non.

defined?(TestFunctions::abc) #=> nil, but
TestFunctions.method_defined? :abc #=> true

Comme je l'ai dit au début, je sais que je suis stupide, est-ce que quelqu'un peut me désensibiliser ?

65voto

Gareth Points 42402

Si vous voulez Module Pour les fonctions de niveau supérieur, définissez-les de l'une des manières suivantes :

module Foo
  def self.method_one
  end

  def Foo.method_two
  end

  class << self
    def method_three
    end
  end
end

Toutes ces façons de faire rendront les méthodes disponibles en tant que Foo.method_one o Foo::method_one etc.

Comme d'autres personnes l'ont mentionné, les méthodes d'instance dans Module sont les méthodes qui sont disponibles dans les endroits où vous avez include d la Module

31voto

Mike Woodhouse Points 27748

Je vais essayer de résumer moi-même les différentes réponses, car chacune d'entre elles avait quelque chose d'intéressant à dire, mais aucune n'est vraiment allée jusqu'à ce que je réalise maintenant que c'est probablement la meilleure réponse :

Je posais la mauvaise question parce que je m'y prenais mal.

Pour des raisons que je ne peux plus expliquer, je voulais un ensemble de fonctions complètement autonomes dans une bibliothèque, qui représentaient les méthodes que j'essayais d'éliminer de mes classes. Cela peut être réalisé, en utilisant des choses comme

module Foo
  def self.method_one
  end

  def Foo.method_two
  end

  class << self
    def method_three
    end
  end

  def method_four
  end

  module_function :method_four
end

Je pourrais aussi include mon module, soit à l'intérieur d'une classe, auquel cas les méthodes deviennent partie intégrante de la classe, soit à l'extérieur, auquel cas elles sont définies sur la classe dans laquelle je m'exécute (Object ? Kernel ? Irb, si je suis interactif ? Probablement pas une bonne idée, alors)

Le fait est qu'il n'y avait aucune bonne raison de ne pas avoir de classe en premier lieu - j'avais en quelque sorte pris un train de pensée qui m'a emmené sur une ligne secondaire rarement utilisée et franchement un peu bizarre. Probablement un retour en arrière, à l'époque où l'OO n'était pas encore très répandu (je suis assez vieux pour avoir passé beaucoup plus d'années à écrire du code procédural).

Les fonctions ont donc été déplacées dans une classe, où elles semblent très heureuses, et les méthodes de la classe ainsi exposées sont joyeusement utilisées partout où cela est nécessaire.

27voto

Marcin Urbanski Points 925

Vous pouvez également utiliser module_function comme suit :

module TestFunctions
  def abc
    puts 123
  end

  module_function :abc
end

TestFunctions.abc  # => 123

Vous pouvez maintenant inclure TestFunctions dans votre classe et appeler "abc" depuis le module TestFunctions.

6voto

Miles Porter Points 41

Je me suis amusé avec ça pendant un moment et j'ai appris plusieurs choses. J'espère que cela pourra aider quelqu'un d'autre. J'exécute Rails 3.2.8.

Mon module (utilities.rb) ressemble à ceci et se trouve dans le répertoire /lib de mon application rails :

module Utilities

  def compute_hello(input_string)
     return "Hello #{input_string}"
  end

end

Mon test (my_test.rb) ressemble à ceci et se trouve dans le répertoire /test/unit de mon application rails :

require "test_helper"
require "utilities"

class MyTest < ActiveSupport::TestCase
  include Utilities

  def test_compute_hello
    x = compute_hello(input_string="Miles")
    print x
    assert x=="Hello Miles", "Incorrect Response"
  end

end

Voici quelques points à noter : Mon test étend ActiveSupport::TestCase. Ceci est important car ActiveSupport ajoute /lib au $LOAD_PATH. (voirhttp://stackoverflow.com/questions/1073076/rails-lib-modules-and)

Deuxièmement, je devais à la fois "require" mon fichier de module et "include" le module. Enfin, il est important de noter que les éléments inclus dans le module sont essentiellement placés dans la classe de test. Donc... faites attention à ce que le module que vous incluez ne commence pas par "test_". Sinon, Rails tentera d'exécuter la méthode de votre module comme un test.

4voto

james Points 305

Vous devez inclure le module

include Testfunctions

Alors 'abc' renverra quelque chose.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X