2017-12-17 1 views
1

TL; DR;
Wie kann ich eine Methode (geschrieben in der Mitte von Nirgendwo) aus einer Ruby-Kernklasse aufrufen?Aufruf einer benutzerdefinierten Methode aus einer Ruby-Kernklasse


Ich schreibe ein Skript, das Textdateien verwaltet. Hier ist mein Code:

File.open("file.txt", "w").each do |f| 
    f.puts "Text to be inserted" 
    f.puts text_generated 
    f.puts "Some other text" if some_condition? 
    f.puts "" 
end 

Ich möchte den Code bereinigen, indem sie ein Verfahren zur Einführung:

File.open("file.txt", "w").each do |f| 
    f.puts_content(text_generated, some_condition? 
    # -> Generates an error: private method called for <File> (NoMethodError) 
end 

def puts_content(text, cond) 
    puts "Text to be inserted" 
    puts text 
    puts "Some other text" if cond 
    puts "" 
end 

Aber in der Tat ist diese Methode nicht aufrufbar aus der File Klasse wegen der privaten Methode Zugang.

Kann jemand diesen Fehler erklären, und wie kann ich das tun?


Meine Abhilfe ist, diese Methoden in einem benutzerdefinierten MyFile Klasse, die von File erbt zu schreiben:

MyFile.open("file.txt", "w").each do |f| 
    f.puts_content # Seems to work 
end 

class MyFile < File 
    def puts_content(cond) 
    puts "Text to be inserted" 
    puts text_generated_elsewhere 
    puts "Some other text" if cond 
    puts "" 
    end 
end 

ich das Zeug direkt in File setzen könnte, aber ich bin ängstlich, wenn es zu berühren kommt eine Sprachkernbibliothek.

Ich möchte wissen, ob dies ein guter Weg ist, es zu tun.


Es ist möglich, Ruby-Core-Methoden von anderen Kernmodulen/Klassen aufzurufen. Bedeutet dies, dass alle Kernmodule/-klassen einander umfassen oder erfordern? Wie funktioniert es unter der Haube?

Antwort

1

Wenn Sie eine Methode auf der obersten Ebene definieren, ist es eine eine Instanzmethode auf Objekt und damit zugänglich für Nachfahre Klassen (die meisten anderen Hauptklassen) hinzugefügt jedoch die Zugriffsebene dieses Methode scheint in IRB/PRY anders zu sein als beim Ausführen eines Skripts.

In IRB:

puts [].foo 
# => 1 

In einem Skript:

puts [].foo 
# => NoMethodError (private method called...) 

Natürlich können Sie immer rufen Sie einfach die private Methode mit send:

[].send(:foo) 
# or, in your case, f.send(:puts_content, text_generated, some_condition?) 

Auch in keiner Fall überschreibt es die Methode für die Nachkommenklasse, wenn sie bereits definiert wurde:

Ihr zweiter Ansatz (direktes Patchen der Core-Klasse) funktioniert und überschreibt den puts_content, wenn er bereits in der Datei definiert wurde (was nicht der Fall war). Allerdings, wenn Sie Core-Klassen Patchen vermeiden wollen, gibt es zwei Ansätze, die ich empfehlen:

  1. eine statische (Klassen-) Methode verwenden und das Dateiobjekt als Argument übergeben

    class FileUtils 
        def self.puts_content(file, text, cond) 
        file.puts "Text to be inserted" 
        file.puts text 
        file.puts "Some other text" if cond 
        file.puts "" 
        end 
    end 
    File.open("file.txt", "w").each do |f| 
        FileUtils.puts_content(f, text_generated, some_condition?) 
    end 
    
  2. eine Verfeinerung verwenden :

    module FileUtils 
        refine File do 
        def puts_content(text, cond) 
         puts "Text to be inserted" 
         puts text 
         puts "Some other text" if cond 
         puts "" 
        end 
        end 
    end 
    
    # elsewhere ... 
    using FileUtils 
    
    File.open("file.txt", "w").each do |f| 
        f.puts_content(f, text_generated, some_condition?) 
    end 
    

    Sie können über Verfeinerungen here, lesen aber im Grunde sind sie eine Art und Weise nur eine Kern-Klasse in einer bestimmten Datei oder Klasse zu patchen. Dies gibt Ihnen den Vorteil dieser netten, knappen Affe-gepatchten Syntax mit weniger Risiko, das anderswo definierte Verhalten zu ändern.

+0

Hallo, ich habe noch nie von Verfeinerungen gehört, danke * (übrigens, [hier ist die Dokumentation] (https://ruby-doc.org/core-2.4.2/doc/syntax/refinements_rdoc.html) für die letzte Version (2.4.2) und Refinements ist kein experimentelles Feature mehr) *. Aber ich mag deinen ersten Vorschlag sehr. Die Gesamtstruktur bleibt genau gleich und die Absicht wird nicht geändert. Ich sollte ein bisschen mehr darüber lernen, wie all das Zeug (statische Klasse) zusammenpasst. Wie wäre es mit einem Modul? Ich habe von Modul gehört, dass es mit anderen Klassen und Methoden "gemischt" werden könnte. – SanjiBukai

+1

Sie können die Methode in ein Modul schreiben (es sieht genauso aus wie die Verfeinerungsdefinition, nur ohne die 'refine File do'-Zeile) und dann schließen Sie es mit 'File.include FileUtils' ein, aber es ist im Grunde dasselbe, als die Dateiklasse wieder zu öffnen, um die Methode hinzuzufügen –

+0

Danke. Es ist klar, die Verwendung einer statischen Klasse ist immer noch meine Lieblingsmethode. Danke nochmal. – SanjiBukai

0

Über Ihre erste Frage. Sie haben den Fehler erhalten, weil die Methode nicht in der Klasse File definiert wurde. Sie konnten es also nicht f.puts_content nennen. Sie können eine Methode definieren, die File als Parameter puts_content(file, ...) empfängt.

Über den zweiten Teil Ihrer Frage, ich das ist eine gute Lösung (Denken objektorientiert).

def foo 
    1 
end 
method(:foo) 
# => #<Method: Object#foo> 

+0

Hallo, ich .. In der Tat nicht daran gedacht, die Datei auf diese Weise zu schreiben, sobald ich offen * * die Datei, die ich viele Stücke von Texten zu schreiben, und sogar von „Verpackung“ sie mit einigen Methoden, Ich habe immer noch viele benutzerdefinierte Methoden .. Also wenn ich das anders machen müsste, sollte ich die Datei jedes Mal öffnen und schließen. Aber es könnte einfacher sein, da es das Zeug auf niedriger Ebene getrennt lässt. Dies führte zu einer anderen Frage: Sagen wir, ich muss 100 Zeilen Text schreiben, ist es vorzuziehen, die Datei einmal zu öffnen und alle Zeilen zu schreiben oder die Datei 100 Mal zu öffnen und zu schließen, eine für jeden Schreibvorgang? * (Angenommen, ich muss 100 verschiedene Zeilen schreiben) * – SanjiBukai

+0

Es ist besser, die Datei einmal zu öffnen. Wenn Sie über die Codestruktur nachdenken, können Sie Ihren Code nicht in viele Funktionen und Klassen aufteilen. –

Verwandte Themen