5

在红宝石中,您可以这样做:

class Thing
  public
  def f1
    puts "f1"
  end

  private
  def f2
    puts "f2"
  end

  public
  def f3
    puts "f3"
  end

  private
  def f4
    puts "f4"
  end
end

现在 f1 和 f3 是公共的,f2 和 f4 是私有的。内部发生了什么允许您调用类方法然后更改方法定义?我怎样才能实现相同的功能(表面上是为了创建我自己的 java 类注解)

例如...

class Thing
  fun
  def f1
    puts "hey"
  end

  notfun
  def f2
    puts "hey"
  end
end

并且 fun 和 notfun 会更改以下函数定义。

谢谢

4

3 回答 3

8

有时你可以将 Ruby 倒入浓缩咖啡杯中。让我们看看如何。

这是一个模块 FunNotFun...

module FunNotFun

  def fun
    @method_type = 'fun'
  end

  def notfun
    @method_type = 'not fun'
  end

  def method_added(id)
    return unless @method_type
    return if @bypass_method_added_hook
    orig_method = instance_method(id)
    @bypass_method_added_hook = true
    method_type = @method_type
    define_method(id) do |*args|
      orig_method.bind(self).call(*args).tap do
        puts "That was #{method_type}"
      end
    end
    @bypass_method_added_hook = false
  end

end

...您可以用来扩展一个类...

class Thing

  extend FunNotFun

  fun
  def f1
    puts "hey"
  end

  notfun
  def f2
    puts "hey"
  end
end

......结果:

Thing.new.f1
# => hey
# => That was fun

Thing.new.f2
# => hey
# => That was not fun

但是请参阅下面的行以获得更好的方法。


注释(请参阅 normalocity 的答案)不太麻烦,并且作为一个常见的 Ruby 习惯用法,将更容易地传达您的代码意图。以下是使用注释的方法:

module FunNotFun

  def fun(method_id)
    wrap_method(method_id, "fun")
  end

  def notfun(method_id)
    wrap_method(method_id, "not fun")
  end

  def wrap_method(method_id, type_of_method)
    orig_method = instance_method(method_id)
    define_method(method_id) do |*args|
      orig_method.bind(self).call(*args).tap do
        puts "That was #{type_of_method}"
      end
    end
  end

end

在使用中,注解出现在方法定义之后,而不是之前:

class Thing

  extend FunNotFun

  def f1
    puts "hey"
  end
  fun :f1

  def f2
    puts "hey"
  end
  notfun :f2

end

结果是一样的:

Thing.new.f1
# => hey
# => That was fun

Thing.new.f2
# => hey
# => That was not fun
于 2011-10-12T23:01:28.787 回答
1

听起来您想为 Ruby 语言本身编写扩展,这是可能的。这不是可以简单解释的东西,但这个链接应该让你开始:

http://ruby-doc.org/docs/ProgrammingRuby/html/ext_ruby.html

此参考与 Ruby 中的注释有关,也可能有帮助/相关:

http://martinfowler.com/bliki/RubyAnnotations.html

于 2011-10-12T22:37:16.167 回答
1

这是一个纯红宝石解决方案,可让您朝着正确的方向前进。它取决于method_added. 小心使用保护子句避免递归。

module Annotations
  def fun
    @state = :fun
  end

  def not_fun
    @state = :not_fun
  end

  def inject_label(method_name)
    state = @state
    define_method(:"#{method_name}_with_label") do |*args, &block|
      puts "Invoking #{method_name} in state #{state}"
      send(:"#{method_name}_without_label", *args, &block)
     end

    alias_method :"#{method_name}_without_label", :"#{method_name}"
    alias_method :"#{method_name}", :"#{method_name}_with_label"
  end

  def self.extended(base)
    base.instance_eval do
      def self.method_added(method_name)
        return if method_name.to_s =~ /_with(out)?_label\Z/
        @seen ||= {}
        unless @seen[method_name]
          @seen[method_name] = true
          inject_label(method_name)
        end
      end
    end
  end
end

class Foo
  extend Annotations

  fun

  def something
    puts "I'm something"
  end

  not_fun

  def other
    puts "I'm the other"
  end
end
于 2011-10-12T23:17:54.153 回答