Domanda

Altro che self.class.send :method, args..., Ovviamente.Mi piacerebbe rendere disponibile un metodo piuttosto complesso sia a livello di classe che di istanza senza duplicare il codice.


AGGIORNAMENTO:

@Jonathan Branam:questa era la mia supposizione, ma volevo assicurarmi che nessun altro avesse trovato un modo per aggirare il problema.La visibilità in Ruby è molto diversa da quella in Java.Hai ragione anche su questo private non funziona sui metodi di classe, anche se questo dichiarerà un metodo di classe privata:

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

Foo.bar
# => NoMethodError: private method 'bar' called for Foo:Class
È stato utile?

Soluzione

Ecco uno snippet di codice per accompagnare la domanda.L'utilizzo di "privato" in una definizione di classe non si applica ai metodi di classe.È necessario utilizzare "private_class_method" come nell'esempio seguente.

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

Non vedo un modo per aggirare questo problema.La documentazione dice che non è possibile specificare la ricezione di un metodo privato.Inoltre puoi accedere solo a un metodo privato dalla stessa istanza.La classe Foo è un oggetto diverso da una determinata istanza di Foo.

Non prendere la mia risposta come definitiva.Non sono certamente un esperto, ma volevo fornire uno snippet di codice in modo che altri che tentano di rispondere abbiano metodi di classe adeguatamente privati.

Altri suggerimenti

Contribuisco a questo elenco di soluzioni e non soluzioni più o meno strane:

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

Se il tuo metodo è semplicemente a funzione utile (ovvero, non si basa su alcuna variabile di istanza), potresti inserire il metodo in un modulo e include E extend la classe in modo che sia disponibile sia come metodo di classe privata che come metodo di istanza privata.

Al giorno d'oggi non hai più bisogno dei metodi di supporto.Puoi semplicemente integrarli con la definizione del tuo metodo.Questo dovrebbe sembrare molto familiare alla gente di 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

E no, non puoi chiamare un metodo di classe privata da un metodo di istanza.Tuttavia, potresti invece implementare the privato metodo di classe come pubblico metodo di classe in a privato classe annidata invece, utilizzando la classe private_constant metodo di aiuto.Vedere questo post sul blog per maggiori dettagli.

Questo è il modo di giocare con i metodi "reali" delle classi private.

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"

saluti

Questo è probabilmente il modo più "nativo di Ruby Vanilla":

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 un po' di metaprogrammazione Ruby, potresti anche farlo sembrare:

class Foo
  def self.foo
    'foo'
  end

  extend PrivateStatic
  private_static :foo
end

La metaprogrammazione di Ruby è piuttosto potente, quindi potresti tecnicamente implementare qualsiasi regola di ambito che potresti desiderare.Detto questo, preferirei comunque la chiarezza e sorpresa minima della prima variante.

A meno che non abbia capito male, non ti serve semplicemente qualcosa del genere:

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

    public
    def bar
        Foo.bar
    end
end

Ovviamente potresti cambiare la seconda definizione per usare il tuo approccio self.class.send se volessi evitare di codificare il nome della classe...

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top