Problem z nadpisaniem gettera

Mam taką klasę:

metoda .modem_macaddr zwraca mi mac adres po konwersji z formatu Integer na string. Wszystko jest ok, poza tym, że kiedy chciałbym wyszukać rekord po modem_macaddr np.:
Docsis::ModemLog.find_by(modem_macaddr: ‘000B06D8FAD4’)
to zapytanie wygląda w ten sposób:
Docsis::ModemLog Load (0.5ms) SELECT dhcp_log.* FROM dhcp_log WHERE dhcp_log.type IN (‘CM’) AND dhcp_log.b_modem_macaddr = 0 LIMIT 1
Czyli jest tylko i wyłącznie rzutowanie .to_i, które zwraca 0. Co powinienem zrobić, żeby móc też wyszukiwać rekordów w taki sposób:
Docsis::ModemLog.find_by(modem_macaddr: ‘000B06D8FAD4’) ?

Nie napisałeś jakiego typu kolumna trzyma MAC adres w bazie patrząc po błędach to jakiś “Integer

Możesz zrzucać String z hex do int używajac przy każdym wywołaniu find_by na wartości pola adresu MAC to_i(16)

Docsis::ModemLog.find_by(modem_macaddr: '000B06D8FAD4'.to_i(16)) 

Jeżeli bazą jest postgresql proponuje skorzystać z kolumny typu macaddr lubi String na wejściu

Pewnie to nie jest postgresql :expressionless: bo byś z tego skorzystał

Tak, w bazie danych MAC przechowywany jest jako INT i nie mogę tego zmienić. Czytam z bazy danych, która jest używana przez działający soft.

Chodziło mi właśnie o to, żeby konwersja dokonywała się już niejawnie wewnątrz modelu tak, żebym nie musiał używać jawnie .to_i, czy .hex wewnątrz find_by. Min po to żebym mógł użyć połączenia has_many z innym modelem w którym niestety MAC adresy przechowywane są jako stringi.
Chyba, że nie ma takiej możliwości?

W tej chwili rozwiązałem to w taki sposób, że zamiast tworzyć relację

has_many :docsis_modem_logs

stworzyłem metodę

def docsis_modem_logs
Docsis::ModemLog.where(modem_macaddr: modem_macaddr.hex)
end

Zastanawia mnie tylko, czy nie można zrobić jednak tego ładniej przez has_many.

Możesz zrobić to przez has many:

has_many :modem_logs_by_macaddr, -> (mac) where(modem_macaddr: mac.hex), class_name: 'Docsis::ModemLog'

Ok, tylko nadal wydaje mi się, że taki kod nie jest do końca DRY.
Wiem, że to trochę czepianie się, ale chciałbym tą konwersję przenieść do modelu ModemLog, żeby nie musieć go powtarzać przy każdej kolejnej relacji.

ależ zwroty akcji :slight_smile:

ja skupiłem się na tym żeby Ci nie konwertował hexa do int 0

@Biscuit ładnie to schował

Mam taką dopytkę, też się czepiam :confused: Docsis::ModemLog to dobra nazwa dla obiektu typu Logger a tu jest ActiveRecord więc czemu nie trzymać tam MAC adresu jako INT?

Konwersji do human readable MAC mógłby dokonywać jakiś Formatter / prosty helper widoku. Możesz zmodyfikować model Docsis::ModemLog ?

Pewnie nie było by Twojego posta :blush: gdybyś mógł poprostu zrobić porządek z HexStringiem w Docsis::ModemLog

No właśnie porządku zrobić nie mogę bo to działa na tabeli z innego programu.
Mógł bym w bazie zrobić widok, który by to konwertował, ale nie wydaj mi się to eleganckim podejściem.

ModemLog jest ActiveRecord ponieważ czyta tabeli do której logi wrzucane są przez ten wcześniejszy soft. Mógł bym to formatować w widoku, ale jest problem z robieniem relacji z innymi obiektami.
Żeby było śmiesznie ten soft(docsis_server) w tabeli do której wrzuca logi operuje na Integerach dla MAC adresów i IP, natomiast w reszcie tabel używa zwykłej notacji w stringach.

Cała zabawa polega dla mnie teraz na tym, żeby zrobić relacje między tymi obiektami, oczywiście można by tak jak pisał @Biscuit, ale, że staram się połączyć przyjemne z pożytecznym i nauczyć się trochę railsów przy okazji pisania przydatnej apki, to chciał bym się dowiedzieć jak to zrobić bardziej DRY :wink:

Zdefinuj scope w modelu log’u

scope :by_mac, -> (mac) where(modem-macaddr: mac.hex)

Potem w modelu wyzej

delegate :by_mac, to: :modem_log, prefix: true

objekt.modem_log_by_mac("12:34")

Przyznam szczerze, że nie pamietam na 100% czy bedzie scopowanie po id modelu ktory ma relacje has_many. Sprawdz czy to tak działa jak pamiętam :slight_smile:

A co sądzicie o takim rozwiązaniu?

def self.find_by(**args)
if args.has_key?(:modem_macaddr)
args[:modem_macaddr] = args[:modem_macaddr].hex
end
super
end

@Snickers przysłonięcie find_by to jak dla mnie krok w tył, pomyśl ile przypadków użycia find_by nie będzie dotyczyło modem_macaddr

jeżeli rozwiązanie @Biscuit Ci się nie podoba

i masz taką sytuację że siedzisz ze swoją apką pomiędzy MAC int a MAC HexString w obcym systemie

to prosisz się o 2 kolumny MAC_int, MAC_hex

i skoro już angażować bazę to nie widokiem konwertującym, tylko uzupełniać MAC_hex triggerem before insert/update

w railsach czysto, gwarancja spójności niezależnie od tego czym wtłoczysz dane do systemu swoją apką inną apką czy sql-owym insertem, is it DRY ?

@DarkCoin masz rację, zresztą nie działa to przy budowaniu relacji.

Z tego wszystkiego najbardziej chyba podoba mi się to co podał @Biscuit na samym początku + ewentualnie metody konwertujące zawarte w concerns, żeby nie powtarzać ich w każdym modelu.

Coś mi jeszcze to nie do końca działa:

has_many :modem_logs_by_macaddr, -> (object) {where(modem_macaddr: object.modem_macaddr.hex)}, class_name: ‘Docsis::ModemLog’

Ale zapytanie jakie jest tworzone jest niepoprawne

Preformatted textDocsis::ModemLog Load (0.4ms) SELECTdhcp_log.* FROMdhcp_logWHEREdhcp_log.typeIN ('CM') ANDdhcp_log.modem_id= '000b06d8faf4' ANDdhcp_log.typeIN ('CM') ANDdhcp_log.modem_macaddr= 47359523572

Oprócz warunku jaki jest w lambda, szuka jeszcze foreign_key w tabeli log(z modelu ModemLog) dhcp_log.modem_id = ‘000b06d8faf4’, który nieistnieje.

Czy można to zmienić tak, żeby brał pod uwagę tylko warunek z lambda?