2. О себе
• Девелопер с 1998 года.
• Писал индустриальные системы на PHP.
• Теперь на RoR.
3. Как определить
неофита?
• Толстые контроллеры
• Беспокойство
• Желание “работать” с предельными
условиями
4. Самый простой
способ?
def foobar
x = self.bar
x = nil if x == 0
y = foo
return x || y
end
5. Return
• Самый неочевидный оператор для
новичков
• Функция его наиболее отличается от
таковой в других языках
6. return для flow-control
def foobar
if foo?
if bar?
#do something
return 42
else
#do something
end
else
#do something
end
end
7. if || unless
if !params[:query].blank?
if !something unless something
8. unless?
• Как ни странно, это единственное для
чего он нужен.
• Читать логику объединенную and/or при
этом применяя к ней ! сложно.
• У него нет elsunless (нельзя сказать, что
это минус).
9. If/else vs. unless
def possible_parents
if self.new_record?
Category.first_level
else
Category.first_level.where('id != ?', self.id)
end
end
vs.
scope :not_given, lambda{|id| where('id != ?', id)}
def possible_parents
Category.first_level
Category.first_level.not_given(self.id) unless self.new_record?
end
10. Виды беспокойства
• Боязнь пользователя (глупого или
злонамерянного).
• Боязнь предельных условий.
• Боязнь языка. Недостаток времени для
экспериментов.
• Иллюзия контроля.
11. Боязнь пользователя
Код из контроллера в админке
@role = Role.find params[:id]
redirect_to role_index_path and return unless @role
Программист боится неправильного параметра.
При этом он забывает о том, что find выбрасывает
exception.
12. Боязнь предельных
условий
session[:foo] = params[:foo].blank? ? nil :
params[:foo].to_i
VS.
session[:foo] = params[:foo] ? params[:foo] : nil
13. Боязнь языка
scope :with_state, lambda {|state| where('state = ?',
state.to_s)}
scope :ready_for_start, with_state(:foo)
scope :ready_for_close, where('state IN(?)',
[:foo, :bar])
“А вдруг он не приведет символ к строке?”
scope :with_states, lambda {|s| where("state in
(#{s.map{|e| "'#{e.to_s}'"}.join(',')})")}
14. Иллюзия контроля
def self.find_for_facebook_oauth(access_token,
signed_in_resource=nil)
data = access_token['extra']['user_hash']
if user = User.find_by_email(data["email"])
user
else # Create a user with a stub password.
user = User.create do |record|
record.email = data['email']
record.password = Devise.friendly_token[0,20]
record.registration_source = 1
record.skip_confirmation!
end
end
end
16. Ослепление
беспокойством 2
def self.to_rtf(codes)
codes.inject("") {|code, buffer| buffer << code.value << "n"}
end
Это был рефакторинг
17. Ослепление
беспокойством 3
def self.to_rtf(codes)
codes.map(&:value).join("n")
end
А это то, что на самом деле было нужно.
18. Знай свои
инструменты
before_filter :guests_only!
def guests_only!
redirect_to root_url and return if user_signed_in?
end
Зачем тут return?
19. Разгадка
• Программист не знает, как работает
before_filter.
• Он беспокоится, что код контроллера
будет выполняться после редиректа в
фильтре.
• От своего беспокойства он забывает как
о том, что нужно возвращать false, так и о
том, что после редиректа это уже лишнее.
20. Более простое
объяснение
• Код был перенесен в фильтр из экшена.
• Программисту просто не хочется его
переписывать, потому как кажется, что
этот возврат нужен (возвращаемся к
исходному объяснению).
21. Metaprogramming to
the recsue
if permission.can?
can permission.action.to_sym, subject
else
cannot permission.action.to_sym, subject
end
vs.
method = permission.can? ? :can : :cannot
send method, permission.action.to_sym, subject
22. Способы борьбы
• Знание языка
• Критический взгляд на свой код
• Let it fail (hoptoad в помощь)