1
votes

Comment puis-je stocker les appels de méthode dans un tableau dans Ruby au lieu de les appeler

Je travaille sur un projet où, dans la suite de tests, nous appelons un tas de méthodes au sein d'une classe Nous devons les conserver, mais ils arrêtent le programme lorsqu'ils sont appelés, donc pour contourner le problème, j'essaie de trouver un moyen de pousser les appels de méthode dans un tableau, afin que je puisse les traiter plus tard.

Le problème est un peu similaire à ceci à ce stade:

class Dog
  assert 1 + 1 =2

end

La question est: y a-t-il un moyen de STOCKER ces appels de méthode dans un tableau, à exécuter plus tard? p>

Edit:

def hello(greeting)
  puts greeting
end

class Dog
  hello "Cat"
  hello "Bear"
  hello "Snake"
  hello "Pig"
  hello "Cow"
  hello "Wolf"
  hello "Lion"

end

c'est plus proche du code avec lequel je travaille, les classes contiennent des assertions qui arrêtent le programme en cas d'échec. Je dois garder la même classe dans la suite de tests, donc je ne peux pas supprimer ces assertions - mais si je veux tester la même chose dans la suite de tests, je ne peux pas les déclencher. C'est pourquoi je veux trouver un moyen d'itérer dans la classe et de pousser les appels de méthode vers un tableau - pour les exécuter après l'exécution de la suite de tests. J'espère que c'est un peu plus clair

Je pense qu'il y a un malentendu. La CLASSE ne peut pas changer. J'essaie de parcourir une classe pour parcourir dynamiquement les instances d'une méthode qui est déjà appelée.

Puis-je parcourir la classe Dog et écrire une méthode pour chaque bonjour?

p>


3 commentaires

"mais ils arrêtent le programme lorsqu'ils sont appelés" Pouvez-vous expliquer plus à ce sujet? Arrêtez comment? une erreur?


Vous pouvez utiliser Procs ou lambdas pour stocker des fermetures dans un tableau, mais votre exemple de code publié ne semble pas justifier la complexité de cette opération. Il serait probablement préférable de stocker les résultats ou les entrées.


La classe est dans notre suite de tests, et la méthode "hello" est en fait une assertion, donc la difficulté à laquelle je suis confronté est d'utiliser l'assert, mais en essayant de la retarder lors de l'écriture des tests. Je suis obligé de conserver les affirmations dans le corps de la classe


3 Réponses :


-1
votes

Pourquoi ne pas simplement créer une autre méthode qui effectue les appels souhaités et appeler cette méthode lorsque cela est nécessaire? Quelque chose comme ceci:

def hello(greeting)
  puts greeting
end

class Dog
  def self.sayHello()
    hello "Cat"
    hello "Bear"
    hello "Snake"
    hello "Pig"
    hello "Cow"
    hello "Wolf"
    hello "Lion"
  end
end

# later 

Dog.sayHello()


2 commentaires

SyntaxError , et lorsque vous corrigez cela, NoMethodError .


Parce que, comme le mentionne l'OP, il n'est pas autorisé à modifier la classe Dog : "Je dois garder la même classe dans la suite de tests."



1
votes

La réponse à la question que vous avez posée est:

hello_targets = %w[Cat Bear Snake Pig Cow Wolf Lion]


# later, whenever you want
hello(args[4]) # puts 'Cow'

hello_targets.each { |target| hello(target) }

Cependant, j'ai l'impression que ce que vous voulez vraiment n'est pas encapsulé dans la question que vous avez posée .

Par exemple, étant donné l'exemple que vous avez publié, il n'y a pas de bonne raison de le faire comme je l'ai publié ci-dessus. Le code ci-dessous est plus simple et fait la même chose.

def hello(greeting)
  puts greeting
end
hello_targets = %w[Cat Bear Snake Pig Cow Wolf Lion]

lazy_greetings = hello_targets.map do |target|
  -> { hello(target) }
end


# later, whenever you want
lazy_greetings[4].call # puts 'Cow'

lazy_greetings.each(&:call)
# Cat
# Bear
# Snake
# Pig
# Cow
# Wolf
# Lion


0 commentaires

2
votes

Je veux trouver un moyen d'itérer dans la classe et de pousser les appels de méthode vers un tableau - pour les exécuter après l'exécution de la suite de tests.

Si vous souhaitez différer un appel de méthode, vous pouvez en effet le stocker dans un tableau. Vous pouvez ensuite utiliser #send pour l'appeler quand vous le souhaitez. Voici un exemple:

require 'singleton'

def hello(method)
  DogWalker.instance.add_method(method)
end

class DogWalker
  include Singleton

  def initialize
    @methods = []
  end

  def add_method(method)
    @methods << method.downcase.to_sym
  end

  def call_methods
    @methods.each do |m| 
      self.respond_to?(m) ? send(m) : send(:etc)
    end
  end

  def cat
    puts 'called Cat'
  end

  def bear
    puts 'called Bear'
  end

  def snake
    puts 'called Snake'
  end

  def pig
    puts 'called Pig'
  end

  def etc
    puts 'called one of the others'
  end
end

class Dog
  hello "Cat"
  hello "Bear"
  hello "Snake"
  hello "Pig"
  hello "Cow"
  hello "Wolf"
  hello "Lion"
end

Dog

DogWalker.instance.call_methods

Un point important est que les noms de méthodes sont stockés en interne dans Ruby sous forme de symboles, donc à chaque fois que vous définissez une méthode, le Le nom de la méthode est stocké sous forme de symbole. Ensuite, vous pouvez utiliser le symbole avec #send pour appeler la méthode. Si vous bricolez un peu cette idée, vous devriez obtenir ce que vous voulez.

Edit: Take Two

J'ai jeté un autre regard sur votre problème, et je pense avoir une meilleure prise en main. il. Vous devez toujours utiliser #send , mais il y a plus.

Vous avez un certain nombre d'appels directs à la méthode hello dans votre classe, ce qui rend certaines choses difficiles. Si vous ajoutez cette ligne:

Dog

à votre code, vous obtiendrez une liste de tous les animaux, un par un.

Si, à la place, vous voulez stocker les appels de méthode à un tableau à appeler plus tard, vous devez trouver un endroit pour placer le tableau. C'est difficile:

  • Vous ne pouvez pas modifier la classe Dog , vous ne pouvez donc pas la placer ici.
  • Vous ne pouvez pas le mettre dans la méthode hello , car il ne conservera pas ses valeurs entre les appels.
  • Vous ne pouvez pas le mettre dans une autre classe puis l'instancier, car vous ne pouvez pas ensuite passer l'instance dans hello ; puisque hello est appelé par la classe et que vous n'êtes pas autorisé à changer de classe, vous ne pouvez pas modifier ces appels pour passer dans l'instance.

Après un peu de bricolage, j'ai trouvé une solution. Ce dont vous avez besoin est une classe qui fait le travail pour vous (nommée de manière appropriée DogWalker ici), instanciée en tant qu'instance singleton:

def a
  puts 'Called a'
end

def b
  puts 'Called b'
end

def c
  puts 'Called c'
end

def d
  puts 'Called d'
end

def my_test
  methods = []
  # assert this, assert that, and
  # if I need to call method a, then
  methods << :a
  # and so on...
  methods << :b
  methods << :c
  methods << :d
end

# Now, to call the methods in the array:

my_test.each { |method| send method }

Pour ce faire , vous devez avoir besoin du module singleton . Ce module expose une méthode :: instance , qui dit essentiellement "S'il existe déjà une instance de cette classe, renvoyez-la. Sinon, créez-en une nouvelle."

La classe DogWalker est exposée comme une instance singleton, en appelant DogWalker :: instance partout où nous la référencons. De cette façon, nous travaillons toujours avec la même instance de DogWalker .

Nous exécutons d'abord Dog , qui appelle le hello code> dans votre classe Dog plusieurs fois. bonjour appelle DogWalker.instance.add_method , qui (crée une instance de DogWalker la première et seulement la première fois que #instance est appelé, et) pousse le symbole de méthode sur le tableau @methods .

Cette variable est conservée entre les appels de méthode à hello ; puisque nous utilisons la méthode instance , nous continuons à travailler avec la même instance de DogWalker . (De cette façon, nous n'avons pas besoin de mettre DogWalker.new dans la méthode hello , ou de le mettre en dehors de la méthode hello et de le passer dans. Le premier continuerait à réinitialiser @methods , et nous ne sommes pas autorisés à faire le second.)

Quand nous aurons terminé, appelons le La méthode call_methods de l'instance DogWalker exécute toutes les méthodes une par une (j'ai juste abrégé avec etc dans l'exemple).

Donc, ceci vous permet d'exécuter votre classe Dog et de différer l'appel de toutes ces méthodes jusqu'à ce que la classe Dog soit terminée.

Les appels de méthode directs dans une classe sont un joli horrible façon de concevoir les choses, mais comme vous le dites, vos mains sont liées et vous devriez pouvoir modifier cela pour faire votre travail.


4 commentaires

Merci, c'est assez utile, le problème que j'ai cependant est que j'essaie d'itérer dans la classe telle quelle et de stocker les appels de méthode, donc si la méthode est appelée 4 fois, elle est poussée 4 fois, si 5 fois , puis 5 méthodes sont stockées.


@LouisRaymond En lisant un peu entre les lignes, je pense avoir compris quel est votre problème et proposer ce que je pense être une solution pour vous.


C'est bien! Merci beaucoup :-)


@LouisRaymond De rien. Je n'ai jamais trouvé d'occasions d'utiliser 'singleton' auparavant, alors j'ai aussi appris quelque chose. Un code bizarre a ses objectifs, je suppose. :)