Ruby определен?( 42[0][:foo] ) && определен?( 93[0][bar] ) == true. Почему?

Короткий рассказ:

«Почему defined?(59[0][:whatever]) оценивается как истина?»


Длинная история:

В последнее время я столкнулся с каким-то странным поведением, которое сбило меня с толку.

Я разрабатывал метод, который выполнял некоторую промывку данных:

#Me washing input data:
def foo(data)
  unless data && defined?(data[0]) && defined?(data[0][:some_param])
    method2(data[0][:some_param])
  else
    freak_out()
  end
end

Обычно я пишу тесты, в которых выбрасываю всевозможные ненужные данные, чтобы убедиться, что ничего странного не происходит:

describe "nice description" do
  it "does not call method2 on junk data" do
    expect(some_subject).to_not receive(:method2)
    random_junk_data_array.each do |junk|
      some_subject.foo(junk)
    end
  end
end

Ну, method2 звали сюда. Это произошло, когда junk был фикснумом.

Я использую ruby 2.1.0, и я вижу, что Fixnum имеет метод #[], который извлекает бит в этой позиции, хорошо.

Но почему fixnum[0][:some_param] считается defined?


person Automatico    schedule 13.05.2014    source источник


Ответы (1)


Выражение defined? проверяет, or not expression ссылается на что-либо распознаваемое (литеральный объект, локальную переменную, которая была инициализирована, имя метода, видимое из текущей области видимости, и т. д.). Возвращаемое значение равно nil, если выражение не может быть разрешено. В противном случае возвращаемое значение предоставляет информацию о выражении.

Поясню на примере: -

defined?("a") # => "expression"
# this returns "method", as there is a method defined on the class String. So, the
# method invocation is possible, and this is a method call, thus returning `"method"`.
defined?("a".ord) # => "method"
# it return nil as there is no method `foo` defined on `"a"`, 
# so the call is not possible.
defined?("a".foo) # => nil

Теперь перейдем к вашей точке зрения: -

Как вы сказали, data[0] дает экземпляр Fixnum, и, конечно же, Fixnum#[] существуют. Таким образом, fixnum_instance[:some_param] также является допустимым вызовом метода. Он просто проверяет, определен ли метод или нет. Если он определен, он скажет да, это выражение "method". В противном случае nil. На самом деле Not проверит, был ли вызов метода успешным или нет.

В Ruby все объекты имеют истинные значения, кроме nil и false, таким образом, "method", будучи строковым объектом, также имеет значение истинных, таким образом, ваше условие выполнено успешно.

person Arup Rakshit    schedule 13.05.2014
comment
Ага. 3[0][:whatever] выдает ошибку, но ошибка не в том, что метод не определен. - person Buck Doyle; 13.05.2014
comment
Я понимаю. По сути, это begin catch, который проверяет только not defined error и возвращает nil, если он его улавливает, или некоторую форму true, если нет. - person Automatico; 13.05.2014