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 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
ja skupiłem się na tym żeby Ci nie konwertował hexa do int 0
@Biscuit ładnie to schował
Mam taką dopytkę, też się czepiam 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 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
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
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) SELECT
dhcp_log.* FROM
dhcp_logWHERE
dhcp_log.
typeIN ('CM') AND
dhcp_log.
modem_id= '000b06d8faf4' AND
dhcp_log.
typeIN ('CM') AND
dhcp_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?