Pregunta

Otro que self.class.send :method, args..., por supuesto.Me gustaría que un método bastante complejo esté disponible tanto a nivel de clase como de instancia sin duplicar el código.


ACTUALIZAR:

@Jonathan Branam:Esa era mi suposición, pero quería asegurarme de que nadie más hubiera encontrado una manera de evitarlo.La visibilidad en Ruby es muy diferente a la de Java.También tienes razón en que private no funciona con métodos de clase, aunque esto declarará un método de clase privado:

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

Foo.bar
# => NoMethodError: private method 'bar' called for Foo:Class
¿Fue útil?

Solución

Aquí hay un fragmento de código que acompaña a la pregunta.El uso de "privado" en una definición de clase no se aplica a los métodos de clase.Debe utilizar "private_class_method" como en el siguiente ejemplo.

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

No veo una manera de solucionar esto.La documentación dice que no se puede especificar la recepción de un método privado.Además, solo puedes acceder a un método privado desde la misma instancia.La clase Foo es un objeto diferente a una instancia determinada de Foo.

No tomes mi respuesta como definitiva.Ciertamente no soy un experto, pero quería proporcionar un fragmento de código para que otros que intenten responder tengan métodos de clase privados adecuados.

Otros consejos

Permítanme contribuir a esta lista de soluciones y no soluciones más o menos extrañas:

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 su método es simplemente un función de utilidad (es decir, no depende de ninguna variable de instancia), podría poner el método en un módulo y include y extend la clase para que esté disponible como método de clase privada y método de instancia privada.

Hoy en día ya no necesitas los métodos auxiliares.Simplemente puede alinearlos con la definición de su método.Esto debería resultarle muy familiar a la gente 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

Y no, no puedes llamar a un método de clase privada desde un método de instancia.Sin embargo, en su lugar podría implementar el privado método de clase como público método de clase en un privado clase anidada en su lugar, usando el private_constant método auxiliar.Ver esta publicación de blog para más detalles.

Esta es la forma de jugar con métodos de clases privadas "reales".

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"

salud

Esta es probablemente la forma más "nativa de Ruby vainilla":

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>

Con algo de metaprogramación de Ruby, incluso podrías hacer que se vea así:

class Foo
  def self.foo
    'foo'
  end

  extend PrivateStatic
  private_static :foo
end

La metaprogramación de Ruby es bastante poderosa, por lo que técnicamente podrías implementar cualquier regla de alcance que desees.Dicho esto, todavía prefiero la claridad y mínima sorpresa de la primera variante.

A menos que esté entendiendo mal, ¿no necesitas algo como esto?

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

    public
    def bar
        Foo.bar
    end
end

Por supuesto, podrías cambiar la segunda definición para usar tu enfoque self.class.send si quisieras evitar codificar el nombre de la clase...

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top