Question

Autre que self.class.send :method, args..., bien sûr.J'aimerais rendre une méthode plutôt complexe disponible à la fois au niveau de la classe et de l'instance sans dupliquer le code.


MISE À JOUR:

@Jonathan Branam :c'était mon hypothèse, mais je voulais m'assurer que personne d'autre n'avait trouvé un moyen de contourner.La visibilité en Ruby est très différente de celle en Java.Tu as aussi tout à fait raison de dire que private ne fonctionne pas sur les méthodes de classe, même si cela déclarera une méthode de classe privée :

class Foo
  class <<self
    private
    def bar
      puts 'bar'
    end
  end
end

Foo.bar
# => NoMethodError: private method 'bar' called for Foo:Class
Était-ce utile?

La solution

Voici un extrait de code pour accompagner la question.L'utilisation de « privé » dans une définition de classe ne s'applique pas aux méthodes de classe.Vous devez utiliser "private_class_method" comme dans l'exemple suivant.

class Foo
  def self.private_bar
    # Complex logic goes here
    puts "hi"
  end
  private_class_method :private_bar
  class <<self
    private
    def another_private_bar
      puts "bar"
    end
  end
  public
  def instance_bar
    self.class.private_bar
  end
  def instance_bar2
    self.class.another_private_bar
  end
end

f=Foo.new
f=instance_bar # NoMethodError: private method `private_bar' called for Foo:Class
f=instance_bar2 # NoMethodError: private method `another_private_bar' called for Foo:Class

Je ne vois pas de moyen de contourner ce problème.La documentation indique que vous ne pouvez pas spécifier la réception d'une méthode privée.De plus, vous ne pouvez accéder qu'à une méthode privée à partir de la même instance.La classe Foo est un objet différent d'une instance donnée de Foo.

Ne prenez pas ma réponse comme définitive.Je ne suis certainement pas un expert, mais je voulais fournir un extrait de code afin que ceux qui tentent de répondre disposent de méthodes de classe correctement privées.

Autres conseils

Permettez-moi de contribuer à cette liste de solutions et non-solutions plus ou moins étranges :

puts RUBY_VERSION # => 2.1.2

class C
  class << self
    private def foo
      'Je suis foo'
    end
  end

  private define_method :foo, &method(:foo)

  def bar
    foo
  end
end

puts C.new.bar # => Je suis foo
puts C.new.foo # => NoMethodError

Si votre méthode est simplement un fonction d'utilité (c'est-à-dire qu'elle ne repose sur aucune variable d'instance), vous pouvez mettre la méthode dans un module et include et extend la classe afin qu'elle soit disponible à la fois en tant que méthode de classe privée et en tant que méthode d'instance privée.

De nos jours, vous n'avez plus besoin des méthodes d'assistance.Vous pouvez simplement les intégrer à la définition de votre méthode.Cela devrait sembler très familier aux utilisateurs de Java :

class MyClass

  private_class_method def self.my_private_method
    puts "private class method"
  end

  private def my_private_method
    puts "private instance method"
  end

end

Et non, vous ne pouvez pas appeler une méthode de classe privée à partir d’une méthode d’instance.Cependant, vous pouvez plutôt implémenter le privé méthode de classe comme publique méthode de classe dans un privé classe imbriquée à la place, en utilisant la private_constant méthode d'assistance.Voir cet article de blog pour plus de détails.

C'est ainsi que l'on peut jouer avec les "vraies" méthodes de classe privée.

class Foo
  def self.private_bar
    # Complex logic goes here
    puts "hi"
  end
  private_class_method :private_bar
  class <<self
    private
    def another_private_bar
      puts "bar"
    end
  end
  public
  def instance_bar
    self.class.private_bar
  end
  def instance_bar2
    self.class.another_private_bar
  end
  def calling_private_method
    Foo.send :another_private_bar
    self.class.send :private_bar
  end
end
f=Foo.new
f.send :calling_private_method 
 # "bar"
 # "hi"
Foo.send :another_private_bar
# "bar"

acclamations

C'est probablement la méthode la plus "native vanilla Ruby":

class Foo
  module PrivateStatic # like Java
    private def foo
      'foo'
    end
  end
  extend PrivateStatic
  include PrivateStatic

  def self.static_public_call
    "static public #{foo}"
  end

  def public_call
    "instance public #{foo}"
  end
end

Foo.static_public_call # 'static public foo'
Foo.new.public_call # 'instance public foo'
Foo.foo # NoMethodError: private method `foo' called for Foo:Class
Foo.new.foo # NoMethodError: private method `foo' called for #<Foo:0x00007fa154d13f10>

Avec un peu de métaprogrammation Ruby, vous pourriez même faire ressembler cela à :

class Foo
  def self.foo
    'foo'
  end

  extend PrivateStatic
  private_static :foo
end

La métaprogrammation de Ruby est assez puissante, vous pouvez donc techniquement implémenter toutes les règles de portée que vous souhaitez.Cela étant dit, je préférerais quand même la clarté et surprise minime de la première variante.

Sauf erreur de compréhension, n'avez-vous pas simplement besoin de quelque chose comme ceci :

class Foo
    private
    def Foo.bar
        # Complex logic goes here
        puts "hi"
    end

    public
    def bar
        Foo.bar
    end
end

Bien sûr, vous pouvez modifier la deuxième définition pour utiliser votre approche self.class.send si vous souhaitez éviter de coder en dur le nom de la classe...

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top