Как включить модуль в класс, чтобы он переопределял класс? - PullRequest
4 голосов
/ 10 марта 2009

Есть ли способ включить модуль в класс, чтобы модуль методы переопределяют методы класса? Например:

module UpcasedName
  def name
    @name.upcase
  end
end

class User
  attr_accessor :name
  include UpcasedName
end

u = User.new
u.name = 'john'
puts u.name # outputs 'john', not 'JOHN'

В приведенном выше примере u.name - это 'john', а не 'JOHN'. Я знаю, что если я расширить пользовательский объект вместо включения модуля в класс, это будет работать

module UpcasedName
  def name
    @name.upcase
  end
end

class User
  attr_accessor :name
end

u = User.new
u.name = 'john'
u.extend UpcasedName
puts u.name # outputs 'JOHN'

Однако я хочу включить модуль на уровне класса, а не на уровне объекта.

Ответы [ 5 ]

3 голосов
/ 10 марта 2009

Прямо сейчас было несколько подходов к этому. Ну, первое и самое основное - это использовать alias_method_chain из ActiveSupport

require 'activesupport'

module UpcasedName
  def self.included( base )
    base.alias_method_chain :name, :upcase
  end

  def name_with_upcase
    @name.upcase
  end
end

class User
  attr_accessor :name
  include UpcasedName
end

u = User.new
u.name = 'john'
puts u.name

Подход, который вы опубликовали, на самом деле похож на подход, описанный здесь методом Брюса Уильямса: http://www.codefluency.com/articles/2009/01/03/wrapping-a-method-in-ruby

Если вы действительно хардкор по этому поводу, вы можете следовать подходам, опубликованным Иегудой Кацем здесь: http://yehudakatz.com/2009/01/18/other-ways-to-wrap-a-method/

2 голосов
/ 10 марта 2009

Включить аналогично наследованию от другого класса в том смысле, что методы класса, в который вы включаете модуль, имеют приоритет над включенными методами. Вы даже можете вызвать super в своем классе, чтобы получить доступ к методу из модуля:

class User
  attr_accessor :name
  def name
    super
  end
  include UpcasedName
end

u = User.new
u.name = 'john'
puts u.name # outputs 'JOHN'

Вот статья об этом: включить или расширить в Ruby

0 голосов
/ 09 августа 2010

Возможно, это не всегда вариант, но я думаю, что лучше просто переместить методы класса в его собственный модуль и смешать этот модуль обратно в класс. Это кажется мне чище.

http://gist.github.com/515856

0 голосов
/ 11 марта 2009

Проблема в том, что attr_accessor создает метод User.name, который переопределяет метод UpcasedName.name, поэтому одно из решений будет использовать attr_writer:

module UpcasedName
  def name
    @name.upcase
  end
end

class User
  attr_writer :name
  include UpcasedName
end

u = User.new
u.name = 'john'
puts u.name # outputs 'JOHN'
0 голосов
/ 11 марта 2009

На основании ответа ucron это можно сделать без активной поддержки следующим образом:

module UpcasedName
  def self.included(base)
    base.send :alias_method, :name_without_feature, :name
    base.send :alias_method, :name, :name_with_upcase
  end

  def name_with_upcase
    @name.upcase
  end
end

class User
  attr_accessor :name
  include UpcasedName
end

u = User.new
u.name = 'john'
puts u.name
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...