Переопределить отдельный метод в экземпляре для вызова метода суперкласса - PullRequest
3 голосов
/ 25 марта 2012

У нас есть две модели Rails: Person и Administrator.Мы запрещаем удаление Administrator s на уровне модели:

class Person < ActiveRecord::Base
end

class Administrator < Person
  def destroy
    raise "Can't remove administrators."
  end
end

me = Administrator.new
me.destroy              # raises an exception

Я хотел бы иметь возможность обойти это во время тестирования, но только для конкретных экземпляров, созданных во время установки и демонтажа.Я не хочу менять поведение класса, поэтому class_eval и remove_method невозможны.

Я попытался переопределить метод #destroy фактического экземпляра:

def me.destroy
  super
end

или переопределите его в классе синглтона:

class << me
  def destroy
    super
  end
end

, но все же возникло исключение.Я не мог понять, как заставить его вызывать метод суперкласса неявно.Я закончил тем, что создал свой собственный метод destroy! (так как на самом деле это не метод в ActiveRecord), который как бы нарушает мое желание не изменять поведение класса:

def destroy!
  ActiveRecord::Persistence.instance_method(:destroy).bind(self).call
end

Есть ли простой способсказать одному экземпляру метода вызвать его метод суперкласса?


Окончательный ответ: На основании статьи, на которую только что ссылался Хольгер, я смог просто явно вызвать метод суперкласса:

def me.destroy
  self.class.superclass.instance_method(:destroy).bind(self).call
end

Ответы [ 2 ]

3 голосов
/ 25 марта 2012

Я бы попытался изменить поведение, чтобы сделать его более удобным для тестирования. Например. Вы можете разрешить необязательный параметр уничтожить, например. i_know_what_im_doing, который должен быть установлен в true, чтобы выполнить уничтожение. В качестве альтернативы вы можете отменить destroy с before_destroy крючком типа

class Administrator < Person
  def before_destroy(record)
    # You can't destroy me
    false
  end
end

В своих тестах вы можете затем позвонить Administrator.skip_callback :before_destroy, чтобы проигнорировать его и получить надлежащее уничтожение.

Наконец, вы можете переписать / заглушить метод в ваших тестах. Хотя вы говорите, что не хотите изменять поведение класса, вы все равно должны это сделать (и неявным образом сделать это с помощью метода destroy! сегодня).

1 голос
/ 25 марта 2012

Я не знаком с метапрограммированием Ruby, поэтому я не буду отвечать, если вы сможете вызвать метод суперкласса в экземпляре, не изменяя его. Но вы можете создать привязку к методу суперкласса с помощью alias:

class Administrator < Person

  alias :force_destroy :destroy 

  def destroy
    raise "Can't remove administrators."
  end
end

При этом admin.destroy вызовет исключение, но admin.force_destroy фактически вызовет уничтожение ActiveRecord.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...