Как реализуется метод «публичный / защищенный / приватный» и как я могу его эмулировать? - PullRequest
5 голосов
/ 13 октября 2011

В ruby ​​вы можете сделать это:

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 и public, f2 и f4 - private. Что происходит внутри, что позволяет вам вызывать метод класса, который затем изменяет определение метода? Как я могу реализовать ту же функциональность (якобы для создания моих собственных java-подобных аннотаций)

например ...

class Thing
  fun
  def f1
    puts "hey"
  end

  notfun
  def f2
    puts "hey"
  end
end

, и fun и notfun изменит следующие определения функций.

Спасибо

Ответы [ 3 ]

8 голосов
/ 13 октября 2011

Иногда можно засунуть Руби в чашку для эспрессо. Посмотрим как.

Вот модуль 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
1 голос
/ 13 октября 2011

Вот чисто рубиновое решение, которое поможет вам в правильном направлении. Это зависит от 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
1 голос
/ 13 октября 2011

Звучит так, будто вы хотите написать расширения для самого языка Ruby, что возможно. Это не то, что можно объяснить кратко, но эта ссылка должна помочь вам начать:

http://ruby -doc.org / документы / ProgrammingRuby / html / ext_ruby.html

Эта ссылка, связанная с аннотациями в Ruby, также может быть полезной / актуальной:

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

...