ruby private-methods access-specifier

Métodos de módulo privado en Ruby



private-methods access-specifier (9)

Tengo una pregunta de dos partes

Mejores prácticas

  • Tengo un algoritmo que realiza alguna operación en una estructura de datos usando la interfaz pública
  • Actualmente es un módulo con numerosos métodos estáticos, todos privados, excepto por el método de interfaz pública.
  • Hay una variable de instancia que debe compartirse entre todos los métodos.

Estas son las opciones que puedo ver, ¿cuál es la mejor ?:

  • Módulo con métodos estáticos (''módulo'' en rubí)
  • Clase con métodos estáticos
  • Módulo Mixin para su inclusión en la estructura de datos
  • Refactoriza la parte del algoritmo que modifica esa estructura de datos (muy pequeña) y crea una mezcla que llama a los métodos estáticos del módulo de algoritmo

Parte técnica

¿Hay alguna forma de hacer un método de Módulo privado ?

module Thing def self.pub; puts "Public method"; end private def self.priv; puts "Private method"; end end

El private allí no parece tener ningún efecto , aún puedo llamar a Thing.priv sin problema.


Una buena manera es como esto

module MyModule class << self def public_method # you may call the private method here tmp = private_method :public end private def private_method :private end end end # calling from outside the module puts MyModule::public_method


Lamentablemente, private solo se aplica a los métodos de instancia. La forma general de obtener métodos privados "estáticos" en una clase es hacer algo como:

class << self private def foo() .... end end

Es cierto que no he jugado con esto en módulos.


También hay Module.private_class_method , que podría decirse que expresa más intención.

module Foo def self.included(base) base.instance_eval do def method_name # ... end private_class_method :method_name end end end

Para el código en la pregunta:

module Thing def self.pub; puts "Public method"; end def self.priv; puts "Private method"; end private_class_method :priv end

Ruby 2.1 o más nuevo:

module Thing def self.pub; puts "Public method"; end private_class_method def self.priv; puts "Private method"; end end


Creo que la mejor manera (y sobre todo la forma en que se escriben las bibliotecas existentes) es hacer una clase dentro del módulo que trata con toda la lógica, y el módulo simplemente proporciona un método conveniente, por ejemplo

module GTranslate class Translator def perform( text ); ''hola munda''; end end def self.translate( text ) t = Translator.new t.perform( text ) end end


module Writer class << self def output(s) puts upcase(s) end private def upcase(s) s.upcase end end end Writer.output "Hello World" # -> HELLO WORLD Writer.upcase "Hello World" # -> so.rb:16:in `<main>'': private method `upcase'' called for Writer:Module (NoMethodError)


El mejor patrón que he encontrado al hacer esto en Rails es renunciar a los módulos que quieren tener métodos privados y usar una clase Singleton en su lugar. No se siente bien, pero funciona y parece más limpio que otros ejemplos que he visto en esta pregunta.

Me encantaría escuchar otras opiniones sobre esto.

Ejemplo:

ErrorService.notify("Something bad happened") class ErrorService include Singleton class << self delegate :notify, to: :instance end def notify(message, severity: :error) send_exception_notification(message) log_message(message, severity) end private def send_exception_notification(message) # ... end def log_message(message, severity) # ... end end


Hacer un módulo privado o clase

Las constantes nunca son privadas. Sin embargo, es posible crear un módulo o clase sin asignarlo a una constante.

Entonces, una alternativa a :private_class_method es crear un módulo o clase privada y definir los métodos públicos en él.

module PublicModule def self.do_stuff(input) @private_implementation.do_stuff(input) end @private_implementation = Module.new do def self.do_stuff(input) input.upcase # or call other methods on module end end end

Uso:

PublicModule.do_stuff("whatever") # => "WHATEVER"

Vea los documentos para Module.new y Class.new .


¿Qué ocurre con el almacenamiento de métodos como lambdas dentro de variables / constantes de clase?

module MyModule @@my_secret_method = lambda { # ... } # ... end

Para prueba:

module A @@C = lambda{ puts "C" } def self.B ; puts "B"; @@C[] ; end private # yeah, this has no sense, just for experiment def self.D ; puts "D"; @@C[] ; end end for expr in %w{ A::B A.B A::C A.C A::D A.D } eval expr rescue puts expr end

Aquí vemos que C puede ser usado con éxito por B y D, pero no desde afuera.


Puede usar el método "incluido" para hacer cosas elegantes cuando se combina un módulo. Esto hace que piense lo que quiere:

module Foo def self.included(base) class << base def public_method puts "public method" end def call_private private_method end private def private_method puts "private" end end end end class Bar include Foo end Bar.public_method begin Bar.private_method rescue puts "couldn''t call private method" end Bar.call_private