Как определить класс в Ruby?
Невозможность определения метода в Ruby довольно проста, я могу просто использовать undef METHOD_NAME
.
Есть ли что-то подобное для класса? Я нахожусь на MRI 1.9.2
.
Мне нужно определить модель ActiveRecord, запустить две строки кода и вернуть модель в исходную форму.
Проблема в том, что у меня есть модель Contact
, и я использую API-интерфейс компании, и бывает, что у них есть класс под названием Contact
, и изменение имени моей модели будет для меня большой работой.
Что я могу сделать в этой ситуации?
Ответы
Ответ 1
>> class Foo; end
=> nil
>> Object.constants.include?(:Foo)
=> true
>> Object.send(:remove_const, :Foo)
=> Foo
>> Object.constants.include?(:Foo)
=> false
>> Foo
NameError: uninitialized constant Foo
EDIT Просто заметил, что вы редактируете, удаление константы - это, вероятно, не лучший способ добиться того, что вы ищете. Почему бы просто не переместить один из классов Contact
в отдельное пространство имен.
EDIT2 Вы также можете временно переименовать свой класс следующим образом:
class Foo
def bar
'here'
end
end
TemporaryFoo = Foo
Object.send(:remove_const, :Foo)
# do some stuff
Foo = TemporaryFoo
Foo.new.bar #=> "here"
Опять же, проблема в том, что у вас все еще будет новый класс Contact
, поэтому вам придется снова его удалить. Я бы порекомендовал вместо этого вместо этого указать свои классы. Это также поможет избежать проблем с загрузкой.
Ответ 2
В подобной ситуации - насмехаясь над классом, используемым внутри другого класса, который я пытаюсь проверить, я нашел это приемлемым решением:
describe TilesAuth::Communicator do
class FakeTCPSocket
def initialize(*_); end
def puts(*_); end
end
context "when the response is SUCCESS" do
before do
class TilesAuth::Communicator::TCPSocket < FakeTCPSocket
def gets; 'SUCCESS'; end
end
end
after { TilesAuth::Communicator.send :remove_const, :TCPSocket }
it "returns success" do
communicator = TilesAuth::Communicator.new host: nil, port: nil, timeout: 0.2
response = communicator.call({})
expect(response["success"]).to eq(true)
expect(response).not_to have_key("error")
expect(response).not_to have_key("invalid_response")
end
end
end
Я бы подумал, что будет лучший способ сделать это - то есть я не мог увидеть способ передать желаемое возвращаемое значение для повторного использования, но пока это кажется достаточно хорошим. Я новичок в насмешках/фабриках, и мне бы хотелось услышать об альтернативных методах.
Edit:
Хорошо, так что не все так похоже.
Я нашел лучший способ использования RSpec mock, благодаря отличное объяснение в RSpec Google Group:
context "with socket response mocked" do
let(:response) do
tcp_socket_object = instance_double("TCPSocket", puts: nil, gets: socket_response)
class_double("TCPSocket", new: tcp_socket_object).as_stubbed_const
communicator = TilesAuth::Communicator.new host: nil, port: nil, timeout: 0.2
communicator.call({})
end
context "as invalid JSON" do
let(:socket_response) { 'test invalid json' }
it "returns an error response including the invalid socket response" do
expect(response["success"]).to eq(false)
expect(response).to have_key("error")
expect(response["invalid_response"]).to eq(socket_response)
end
end
context "as SUCCESS" do
let(:socket_response) { 'SUCCESS' }
it "returns success" do
expect(response["success"]).to eq(true)
expect(response).not_to have_key("error")
expect(response).not_to have_key("invalid_response")
end
end
end