Jak mogę zainicjować zmienne instancji modułu w Rubim?

80

Mam kilka modułów, w których chciałbym użyć zmiennych instancji. Obecnie inicjuję je w ten sposób:

module MyModule
  def self.method_a(param)
    @var ||= 0
    # other logic goes here
  end
end

Mógłbym również wywołać metodę init, aby je zainicjować:

def init
  @var = 0
end

ale to oznaczałoby, że muszę pamiętać, aby zawsze to nazywać.

Czy jest lepszy sposób na zrobienie tego?

Geo
źródło
3
Czy pierwszy blok kodu nie ustawia zmiennej instancji modułu (zmienna w zakresie obiektu typu MyModule), podczas gdy druga ustawia „zwykłą” instancję var (zmienną w zakresie instancji zawierającej moduł)? Myślę, że te dwa bloki kodu nie robią tego samego ...
Boris B.

Odpowiedzi:

109

Zainicjuj je w definicji modułu.

module MyModule
  # self here is MyModule
  @species = "frog"
  @color = "red polka-dotted"
  @log = []

  def self.log(msg)
    # self here is still MyModule, so the instance variables are still available
    @log << msg
  end
  def self.show_log
    puts @log.map { |m| "A #@color #@species says #{m.inspect}" }
  end
end

MyModule.log "I like cheese."
MyModule.log "There's no mop!"
MyModule.show_log #=> A red polka-dotted frog says "I like cheese."
                  #   A red polka-dotted frog says "There's no mop!"

Spowoduje to ustawienie zmiennych instancji, gdy moduł jest zdefiniowany. Pamiętaj, że możesz później ponownie otworzyć moduł, aby dodać więcej zmiennych instancji i definicji metod lub przedefiniować istniejące:

# continued from above...
module MyModule
  @verb = "shouts"
  def self.show_log
    puts @log.map { |m| "A #@color #@species #@verb #{m.inspect}" }
  end
end
MyModule.log "What's going on?"
MyModule.show_log #=> A red polka-dotted frog shouts "I like cheese."
                  #   A red polka-dotted frog shouts "There's no mop!"
                  #   A red polka-dotted frog shouts "What's going on?"
rampion
źródło
12
Minęło trochę czasu, ale tak :). Poza tym czuję, że wszystko, co trzeba powiedzieć o panach Foo i Barach, zostało powiedziane.
rampion
Czy w tym momencie nie są to zmienne instancji, ale metody instancji?
nipponese
nipponese: czym nie są zmienne instancji?
rampion
5

Możesz użyć:

def init(var=0)
 @var = var
end

I domyślnie będzie to 0, jeśli nic nie przekażesz.

Jeśli nie chcesz za każdym razem dzwonić, możesz użyć czegoś takiego:

module AppConfiguration
   mattr_accessor :google_api_key
   self.google_api_key = "123456789"
...

end
Brian
źródło
Więc nadal muszę wywołać tę funkcję przed zrobieniem czegokolwiek z modułem?
Geo
Dodałem drugi sposób, aby uniknąć tego problemu - pochodzi z kodu szyn
Brian
2

dla klasy, powiedziałbym, co następuje, ponieważ inicjalizacja jest wywoływana za każdym razem, gdy .new jest nową instancją klasy.

def initialize
   @var = 0
end

z Practical Ruby :

Dalej mówi się, że inicjalizacja modułu zostanie wywołana, jeśli inicjalizacja klasy zawierającej wywoła super, ale nie wspomina, że ​​jest to konsekwencja tego, jak działa super wszędzie, a nie specjalna obsługa inicjalizacji. (Dlaczego można by założyć, że inicjalizacja jest obsługiwana w specjalny sposób? Ponieważ jest obsługiwana w specjalny sposób pod względem widoczności. Specjalne przypadki powodują zamieszanie).


źródło
1
Skoro jest to moduł, to czy inicjalizacja zostanie wywołana? Oto moja pierwsza odpowiedź. Dodałem drugą, aby wyjaśnić, że nie jest wywoływana.
Brian
1
OP konkretnie wspomniane moduły, a nie zajęcia
Zensaburou
2

Odpowiedziałem podobną pytanie , można ustawić zmienne instancji klasy w ten sposób

module MyModule
  class << self; attr_accessor :var; end
end

MyModule.var
=> nil

MyModule.var = 'this is saved at @var'
=> "this is saved at @var"

MyModule.var    
=> "this is saved at @var"
Orlando
źródło
2

Najwyraźniej inicjowanie zmiennych instancji w module w Rubim jest złą formą. (Z powodów, których w pełni nie rozumiem, ale dotyczą kolejności, w jakiej tworzone są elementy).

Wydaje się, że najlepszą praktyką jest używanie akcesorów z leniwą inicjalizacją, na przykład:

module MyModule
  def var
    @var ||= 0 
  end
end

Następnie użyj varjako gettera dla @var.

Andy
źródło
4
Zauważ, że nigdy nie pozwoli ci to ustawić @var na nilorfalse
steel