Подтвердить что ты не робот

Как определить класс в Ruby?

Невозможность определения метода в Ruby довольно проста, я могу просто использовать undef METHOD_NAME.

Есть ли что-то подобное для класса? Я нахожусь на MRI 1.9.2.

Мне нужно определить модель ActiveRecord, запустить две строки кода и вернуть модель в исходную форму.

Проблема в том, что у меня есть модель Contact, и я использую API-интерфейс компании, и бывает, что у них есть класс под названием Contact, и изменение имени моей модели будет для меня большой работой.

Что я могу сделать в этой ситуации?

4b9b3361

Ответ 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