如何在模块中包含模块以使模块覆盖类?

时间:2009-03-10 05:43:37

标签: ruby-on-rails ruby

有没有办法将模块包含在类中,以便模块可以 方法是否覆盖了类的方法?例如:

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 个答案:

答案 0 :(得分:3)

目前有几种方法可以做到这一点。那么第一个也是最基本的是使用ActiveSupport的alias_method_chain

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

如果你真的对此有所了解,可以按照Yehuda Katz发布的方法进行操作:http://yehudakatz.com/2009/01/18/other-ways-to-wrap-a-method/

答案 1 :(得分:2)

Include类似于从另一个类继承,在某种意义上,包含模块的类的方法优先于包含的方法。您甚至可以在班级中调用super来访问模块中的方法:

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

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

以下是关于它的文章:include vs. extend in Ruby

答案 2 :(得分:0)

根据ucron的回答,可以在没有activesupport的情况下执行此操作,如下所示:

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

答案 3 :(得分:0)

这里的问题是attr_accessor创建一个覆盖UpcasedName.name方法的User.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'

答案 4 :(得分:0)

它可能并不总是一个选项,但我认为如果你只是将类的方法移动到它自己的模块中并将该模块混合回类中会更好。这让我觉得更清洁。

http://gist.github.com/515856