2011-01-19 21 views
5

Mam wiele modeli i relacji. W związku z tym, istnieje wiele połączeń w widokach/sterowników, które wyglądają tak:Jaki jest właściwy sposób sprawdzania istnienia obiektów w Railsach/Ruby?

@object.something.with_something.value 

Część łańcucha może skończyć się zerowa, co jest zupełnie ok. Jaki jest właściwy/czysty/szybki sposób sprawdzenia istnienia obiektu terminalu?

dzwoni coś takiego:

@object.something.with_something.value if defined? @object.something.with_something.value 

uważane za ok?

+0

To pytanie nie brzmi bardzo wyraźnie ... Czy interesuje Cię tylko '' object.something.with_something.value' 'nil' lub czy martwisz się' NoMethodError', który zostanie podniesiony, jeśli cokolwiek w łańcuch to 'nil'? Zakładam to drugie? –

Odpowiedz

8

W tym celu należy użyć operatora && (nie defined?), ale może to być bardzo szczegółowe, bardzo szybko.

Więc zamiast robić to:

(@object && @object.something && @object.something.with_something && 
    @object.something.with_something.value) 

Można to zrobić, gdy ActiveSupport jest obecny:

@object.try(:something).try(:with_something).try(:value) 

Albo zainstalować invocation construction kit i wykorzystać swoje strzeżone narzędzia oceny:

Ick::Maybe.belongs_to YourClass 
maybe(@object) { |obj| obj.something.with_something.value } 
2

what.you.are.doing jest czasami nazywany "Wrakiem pociągu". Jest to również opisane jako naruszenie prawa Demeter.

Sądzę, że jest coś, co nazywa się "andand", co może pomóc w tym, co robisz.

+1

Tak, 'Obiekt # andand' to jedno rozwiązanie, ale staram się tego uniknąć - trochę dziwnie jest wprowadzić zależność, która zasadniczo daje jedną metodę.Ponadto, małpa łata domyślnie klasę 'Object', co jest okropne. –

3

Najlepiej rozmieścić resztę kodu, aby zobaczyć ten problem najwyżej ostatni obiekt w łańcuchu.

defined? nie zrobi tego, co chcesz. Coś może być jednocześnie defined? i nil.

Gdy problem jest ograniczony do ostatniego atrybutu w łańcuchu dokumentów:

@object.something.with_something.value if @object.something.with_something 

mogę skorzystać z faktów, że:

nil.to_a => [] 
nil.to_s => '' 
nil.to_f => 0.0 
nil.to_i => 0 

Tak więc, jeśli wiesz, że coś jest albo nil lub Array, często można napisać lepszy kod bez żadnych warunkowych w ogóle, pisząc coś w stylu:

something.to_a.each do |e| 
    . . . 
+0

Tak, ponownie przeczytałem OP i zorientowałem się, co masz na myśli. Wczytałem się trochę w pytanie OP, ponieważ wiedziałem, że pytanie, na które odpowiedziałem, było bardziej prawdopodobne, że sprawił mu kłopot. –

+2

Szczerze mówiąc, nie jestem jednak wielkim fanem twojego rozwiązania. Widziałem go kilka razy i usuwam go, kiedy tylko go zobaczę, ponieważ czuję się dość mocno, że sprawia, że ​​kod jest mniej czytelny. Inne metody, takie jak 'try' i' maybe' rozwiązują problem bardziej ogólnie i robią to w bardziej czytelny sposób. –

+0

Wiem, co masz na myśli, ale kiedy dowiedziałem się, że 'nil' wyspecjalizował funkcje' Object' to_ * x *, pomyślałem: * Matz jest naprawdę geniuszem. * Na przykład ** (something.somethingelse || [])., każdy z nich ** jest w pewnym sensie nieporęczny w porównaniu. – DigitalRoss

0

Inną opcją jest użycie Null Object pattern, aby upewnić się, że żaden z tych obiektów nie jest nigdy zerowy. Prawdopodobnie, jeśli twój kod będzie miał dostęp do łańcucha w ten sposób, to zawsze powinno być zdefiniowane coś, co jest.

+0

Najlepszą częścią tego jest to, że w Ruby ten wzór może być skutecznie zaimplementowany w sposób przezroczysty. Zasadniczo to, jak zestaw do inwokacji wdraża "być może" pod maską. Zapewnia 'IdentityWrapper' i' GuardWrapper'. "GuardWrapper" ma w zasadzie ten sam cel, co obiekt zerowy w tym wzorze, z tym wyjątkiem, że opakowanie może zostać automatycznie usunięte po zakończeniu sprawdzania bloku. –

Powiązane problemy