2017-01-22 1 views
0

Ich habe ein Modell, das eine Zeitlänge (Minuten und Sekunden Spalten) als Teil seiner Daten hat. Ich möchte eine Methode hinzufügen, um die Zeit als mm: ss (wie 09:08) zurückgeben. Ich habe eine Methode dafür gemacht und es mit irb getestet. Aber wenn ich versuche, auf den Wert aus meiner Sicht zuzugreifen, sieht es so aus, als ob die Instanzvariablen nicht initialisiert werden und ich bekomme "00:00" für alle Werte. Wenn ich hier und anderswo hinschaue, sieht es so aus, als könnte ich eine Klassenmethode erstellen (indem ich self hinzufüge) und den Wert zur Formatierung übergeben, aber das scheint überflüssig zu sein. Die Modellinstanz sollte bereits die Daten enthalten. Verwendet eine Klassenmethode den Rubin-Weg? Oder mache ich etwas anderes offensichtlich falsch?Kann ich eine Instanzmethode für einen berechneten Wert in einem Schienenmodell verwenden?

class Segment < ApplicationRecord 
    belongs_to :course 

    def time 
    m = pad @min 
    s = pad @sec 
    m + ":" + s 
    end 

    def pad(x) 
    x.to_s.rjust(2,"0") 
    end 
end 

view: 
    <%= segment.time %> # returns 00:00 
    <%= segment.min %>:<%= segment.sec %> # returns correct non-padded values 

Ich verwende Rails 5.0.1 und Ruby 2.2.6p386.

+0

Ich denke nicht, dass Instanzvariablen das Problem sind. Ich denke, es ist etwas über die Art und Weise, wie Sie die Saite in 'time' bauen. Probieren Sie die String-Interpolation aus, wie eine Antwort vermuten lässt. –

+0

In Bezug auf Klassenmethoden können Sie ganze Programme schreiben, die alles als Argument übergeben und keinen Instanzstatus speichern. Thid dauert länger, um zu schreiben, aber jede Methode kann eigenständig sein kann helfen, Ihren Code zu entkoppeln. Am besten versuchen Sie beide Ansätze und sehen Sie, welche Sie bevorzugen –

Antwort

1

Das Problem besteht darin, dass Sie versuchen, auf ein ActiveRecord-Attribut zuzugreifen, mit der Annahme, dass jedes Datenfeld in einer Instanzvariable gespeichert ist. Dies funktioniert nicht, da die Attribute in @attributes als ActiveRecord::AttributeSet Struktur gespeichert sind.

Sie können einen Blick auf die Struktur nehmen segment.instance_method_get(:@attributes)

Manipulieren die Rohdaten innerhalb dieser Struktur ziemlich mühsam ist. Daher definiert AR Attributmethoden für jedes Attribut in der Klasse. In Ihrem Fall, #min, #min= oder #sec, #sec= (und eine Reihe anderer Methoden).

Es empfiehlt sich, auf Daten über Zugriffsmethoden zuzugreifen, anstatt direkt auf die Instanzvariable zuzugreifen. Dies gilt unabhängig davon, ob Sie die Methode von innerhalb oder außerhalb der Klasse aufrufen. Wenn Sie die Methode außerhalb der Klasse aufrufen, müssen Sie explizit den Empfänger der Zugriffsmethode angeben, die Ihr -Objekt sein wird.

segment.min 

Innerhalb Ihrer Klasse wird der Empfänger als self implizit definiert (als Michael Kohl erwähnt), so dass Sie die Accessor direkt

class Segment 
    def padded_min 
    min to_s.rjust(2,"0") 
    end 
end 

Implizite Definition funktioniert ein wenig anders für Verfahren, welches Ende mit aufrufen mit = - Ruby geht davon aus, dass Sie eine lokale Variable erstellen. Sie müssen den Empfänger explizit definieren, wenn Sie versuchen, eine Setter-Methode aufzurufen.

class Segment 
    def update_time(time) 
    self.time = (time) 
    end 
end 
+0

Sie haben die Wurzel meiner Verwirrung angesprochen! Ich danke dir sehr! –

1

Warum versuchen Sie, Instanzvariablen zu verwenden? Wenn min und sec Spalten Ihrer Tabelle sind, können Sie sie einfach direkt verwenden (der implizite Empfänger ist self, also ist es eine kürzere Art zu sagen self.min und self.sec).

Dies sollte funktionieren:

def time 
    "#{pad(min)}:#{pad(sec)}" 
end 

Im Allgemeinen obwohl ich in mit Helfern und/oder Dekorateure aussehen würde raten, verursachen, was Sie tun fügt jetzt zwei Methoden, um das Modell, das für die Anzeige nur verwendet werden, Zwecke.

+0

Vielen Dank. Das funktioniert. –

2

Es gibt eine viel elegantere Art, die Zeitdauer zu verarbeiten.

Verwenden Sie einfach eine einzelne Integer-Spalte und speichern Sie die Dauer in der kleinsten benötigten Genauigkeitsstufe (Sekunden).In diesem Beispiel rufen wir die Spalte duration auf.

class Segment < ApplicationRecord 
    # custom getter which returns an ActiveSupport::Duration 
    def duration 
    self[:duration].seconds 
    end 

    def duration_as_time 
    Time.at(self[:duration]).utc 
    end 

    def time 
    duration_as_time.strftime("%M:%S") 
    end 
end 

Beachten Sie, dass wir die eckige Klammer verwenden Accessoren []. Mit Ruby können Sie diese Zugriffsmethoden für jede Klasse definieren. In ActiveRecord werden sie zum Abrufen/Festlegen des Spaltenwerts verwendet.

Die duration_as_time Methode gibt uns ein Time-Objekt, das die Unix-Epoche ist und die Dauer:

irb(main):025:0> Time.at(360).utc 
=> 1970-01-01 00:06:00 UTC 

Mit uns speziell dafür gebauten Methoden wie strftime statt das Rad neu erfinden ein Zeitobjekt läßt.

Es kann jedoch argumentiert werden, dass Formatierung Zeit ist nicht die Aufgabe des Modells und sollte von einem Helfer oder Dekorateur behandelt werden. Dies gilt insbesondere, wenn Sie localizing times sind, da Modelle nicht Gebietsschema bewusst sind.

+0

Danke! Ich werde das definitiv nutzen, um das, was ich habe, umzugestalten. –

Verwandte Themen