Frage

Außer self.class.send :method, args..., Natürlich.Ich möchte eine ziemlich komplexe Methode sowohl auf Klassen- als auch auf Instanzebene verfügbar machen, ohne den Code zu duplizieren.


AKTUALISIEREN:

@Jonathan Branam:Das war meine Annahme, aber ich wollte sicherstellen, dass niemand sonst einen Ausweg gefunden hatte.Die Sichtbarkeit in Ruby unterscheidet sich stark von der in Java.Da hast du auch völlig Recht private funktioniert nicht mit Klassenmethoden, obwohl dadurch eine private Klassenmethode deklariert wird:

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

Foo.bar
# => NoMethodError: private method 'bar' called for Foo:Class
War es hilfreich?

Lösung

Hier ist ein Codeausschnitt, der zu der Frage passt.Die Verwendung von „private“ in einer Klassendefinition gilt nicht für Klassenmethoden.Sie müssen „private_class_method“ wie im folgenden Beispiel verwenden.

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

Ich sehe keinen Weg, das zu umgehen.In der Dokumentation heißt es, dass Sie den Empfang einer privaten Methode nicht angeben können.Außerdem können Sie nur von derselben Instanz aus auf eine private Methode zugreifen.Die Klasse Foo ist ein anderes Objekt als eine bestimmte Instanz von Foo.

Betrachten Sie meine Antwort nicht als endgültig.Ich bin sicherlich kein Experte, aber ich wollte einen Codeausschnitt bereitstellen, damit andere, die versuchen zu antworten, ordnungsgemäß private Klassenmethoden haben.

Andere Tipps

Lassen Sie mich zu dieser Liste mehr oder weniger seltsamer Lösungen und Nichtlösungen beitragen:

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

Wenn Ihre Methode lediglich eine ist Utility-Funktion (das heißt, es ist nicht auf Instanzvariablen angewiesen), Sie könnten die Methode in ein Modul einfügen und include Und extend die Klasse, sodass sie sowohl als private Klassenmethode als auch als private Instanzmethode verfügbar ist.

Heutzutage braucht man die Hilfsmethoden nicht mehr.Sie können sie einfach in Ihre Methodendefinition integrieren.Das dürfte den Java-Leuten sehr bekannt vorkommen:

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

Und nein, Sie können eine private Klassenmethode nicht von einer Instanzmethode aus aufrufen.Sie könnten jedoch stattdessen das implementieren Privat Klassenmethode als öffentlich Klassenmethode in a Privat stattdessen eine verschachtelte Klasse mit der private_constant Hilfsmethode.Sehen dieser Blogbeitrag für weitere Einzelheiten.

Dies ist die Art und Weise, mit „echten“ Methoden privater Klassen zu spielen.

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"

Prost

Dies ist wahrscheinlich die „nativste Vanilla Ruby“-Methode:

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>

Mit etwas Ruby-Metaprogrammierung könnten Sie es sogar so aussehen lassen:

class Foo
  def self.foo
    'foo'
  end

  extend PrivateStatic
  private_static :foo
end

Rubys Metaprogrammierung ist ziemlich leistungsfähig, sodass Sie alle gewünschten Scoping-Regeln technisch implementieren können.Davon abgesehen würde ich immer noch die Klarheit bevorzugen minimale Überraschung der ersten Variante.

Sofern ich das nicht falsch verstehe, brauchen Sie nicht einfach so etwas:

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

    public
    def bar
        Foo.bar
    end
end

Natürlich könnten Sie die zweite Definition ändern, um Ihren self.class.send-Ansatz zu verwenden, wenn Sie eine harte Codierung des Klassennamens vermeiden möchten ...

Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit StackOverflow
scroll top