Я дивився Санді Меца " ніщо - це щось презентація кілька разів.Я усвідомлюю, що в своїх проектах на рейках всюди роблю перевірки на нуль, але я не знаю, як уникнути перевірок на нуль і як це зробити об’єктно-орієнтовано, коли йдеться про асоціації.
Розглянемо ці асоціації:
#app/models/user.rb
class User < ActiveRecord::Base
belongs_to :blog
end
#app/models/blog.rb
class Blog < ActiveRecord::Base
belongs_to :hash_tag
has_one :user
end
#app/models/hash_tag.rb
class HashTag < ActiveRecord::Base
has_one :blog
end
Я захоплюю користувача:
@user = User.find(1)
І я хочу знайти його блог:
@user.blog
=> nil
Це повертається nil
ось тому, що це user
трапляється, що не пов’язано blog
, отже, наступний код порушить роботу програми, якби я зробив щось подібне для цього user
:
@user.blog.title
=> undefined method `title" for nil:NilClass
Просте виправлення полягає в тому, щоб зробити це не об’єктно-орієнтованим способом і просто виконати перевірку на нуль (але цього ми хочемо уникнути, оскільки в іншому випадку перевірки на нуль є абсолютно скрізь у програмі):
@user.blog.title if @user.blog
Виконання нульових перевірок стає більш громіздким і процедурним, чим глибше ви проникаєте через асоціації, як показано нижче:
@user = User.find(1)
if @user.blog && @user.blog.hash_tag #checking for nil twice
@user.blog.hash_tag.tag_name
end
Який об’єктно-орієнтований спосіб уникнути нульових перевірок асоціацій?
Мені відомі рейки " спробувати метод, хоча Мец, здається, не рекомендував try
метод. Можливо, якщо мова йде про асоціації в рейки: try
це найкращий варіант?
Відповіді:
6 за відповідь № 1Існує так звана настанова щодо програмування Закон Деметри
Ось як застосувати його до своїх моделей рейок:
#app/models/user.rb
class User < ActiveRecord::Base
belongs_to :blog
delegate :title, to: :blog, prefix: true, allow_nil: true
# then you can call user.blog_title
delegate :tag_name, to: :blog, prefix: true, allow_nil: true
# follow LoD
end
#app/models/blog.rb
class Blog < ActiveRecord::Base
belongs_to :hash_tag
delegate :tag_name, to: :hash_tag, allow_nil: true
has_one :user
end
І тепер ви можете зробити це:
@user = User.find(1)
@user.blog_title # no error even if there is no associated blog
@user.tag_name # no error even if there is no associatd blog or no associated hash_tag object
Будь ласка, прочитайте наступне посилання для посилань:
- http://apidock.com/rails/Module/delegate
- http://samurails.com/tutorial/rails-delegate-dont-break-the-law-of-demeter/
2 для відповіді № 2
Є чудова перлина andand
який я використовую у подібних випадках. Зазвичай вам потрібно написати:
@user.blog && @user.blog.title
andand
gem реалізує загальний нульовий шаблон. andand
розширює Object методом andand
, який повертає об'єкт, до якого його викликали, якщо він не дорівнює нулю. У разі значення nil повертається об'єкт NullPatern, щоб повернути значення nil для всіх викликів методів, використовуючи метод magic_missing magic.
@user.blog.andand.title
Це ще потужніше, коли потрібно перевірити більше умов:
@user.blog && @user.blog.title && @user.blog.title.capitalize
стає:
@user.blog.andand.title.andand.capitalize
1 для відповіді № 3
Я думаю, що це можуть бути варіанти
Я б хотів:
rescue
метод
Обережно: цей метод вловлює винятки і відповідає відповідно; Не використовуйте це, якщо вам потрібно по-іншому реагувати на винятки. Сподіваюся, ви розумієте
@user = User.find(1)
tag_name = @user.blog.hash_tag.tag_name rescue "" # respond with default value
Приклад:
2.1.1 :001 > var1 = nil
=> nil
2.1.1 :002 > my_val = var1.some_method
NoMethodError: undefined method `some_method" for nil:NilClass
from (irb):2
from /home/shiva/.rvm/rubies/ruby-2.1.1/bin/irb:11:in `<main>"
2.1.1 :003 > my_val = var1.some_method rescue ""
=> ""
Іншим варіантом було б .try
метод; http://apidock.com/rails/Object/try
.try
визначено вRails
тому не бути доступним позаRails
@person.non_existing_method if @person.respond_to?(:non_existing_method)
# instead use
@person.try(:non_existing_method) # => returns nil if does not respond_to
try повертає nil при виклику nil незалежно від того, чи відповідає він методу:
nil.try(:to_i) # => nil, rather than 0