13

Ruby で、FooFoo の大規模なコレクションをカタログ化できるクラスがあるとします。すべての Foo が緑色で球状であるというのは基本的な自然の法則であるため、次のようにクラス メソッドを定義しました。

class Foo
  def self.colour
    "green"
  end

  def self.is_spherical?
    true
  end
end

これは私がすることができます

Foo.colour # "green"

だがしかし

my_foo = Foo.new
my_foo.colour # Error!

my_fooそれは明白に緑色であるという事実にもかかわらず。

明らかに、 をcolour呼び出すインスタンス メソッドを定義できますself.class.colourが、そのような基本的な特性が多数あると扱いにくくなります。

欠落しているメソッドのクラスを試すように定義することでおそらくそれを行うこともできますがmethod_missing、これが私がやるべきことなのか醜いハックなのか、それとも安全に行う方法なのかはわかりません (特に私は実際に ActiveRecord の下にいるため) Rails では、method_missing を使っていくつかの Clever Fun Stuff を実行していることを理解しています)。

あなたは何をお勧めします?

4

8 に答える 8

24

Rubyに付属するForwardableモジュールは、これをうまく実行します。

#!/usr/bin/ruby1.8

require 'forwardable'

class Foo

  extend Forwardable

  def self.color
    "green"
  end

  def_delegator self, :color

  def self.is_spherical?
    true
  end

  def_delegator self, :is_spherical?

end

p Foo.color                # "green"
p Foo.is_spherical?        # true
p Foo.new.color            # "green"
p Foo.new.is_spherical?    # true
于 2010-07-09T17:31:23.580 に答える
21

普通の Ruby ならForwardableを使うのが正解

Rails の場合は、 delegate を使用します

class Foo
  delegate :colour, to: :class

  def self.colour
    "green"
  end
end

irb(main):012:0> my_foo = Foo.new
=> #<Foo:0x007f9913110d60>
irb(main):013:0> my_foo.colour
=> "green"
于 2014-05-21T22:27:29.290 に答える
4

モジュールを使用できます:

module FooProperties
  def colour ; "green" ; end
  def is_spherical? ; true ; end
end

class Foo
  extend FooProperties
  include FooProperties
end

少し醜いですが、使用するよりはましmethod_missingです。他の回答に他のオプションを入れてみます...

于 2010-01-28T16:47:59.390 に答える
4

デザインの観点から言えば、答えはすべて同じですがFoos、色と球体ですか? のインスタンスのプロパティであるFooため、クラス メソッドではなくインスタンス メソッドとして定義する必要があります。

ただし、この動作が必要な場合もあります。たとえばBars、システム内にすべてが青色であり、コードのどこかにクラスが渡され、呼び出す前にインスタンスの色を知りたい場合などです。newクラスで。

また、ActiveRecord がダイナミック ファインダーなどのために広範囲に使用することは正しいmethod_missingので、そのルートを下った場合、メソッド名がそれができるものではないと判断した場合、method_missing がスーパークラスからのものを呼び出したことを確認する必要があります。自体を処理します。

于 2010-01-28T16:55:59.830 に答える
3

これを行う最善の方法は、Dwemthy の配列メソッドを使用することだと思います。

調べて詳しく記入していきますが、スケルトンはこちら

編集:イェーイ!働く!

class Object
  # class where singleton methods for an object are stored
  def metaclass
    class<<self;self;end
  end
  def metaclass_eval &block
    metaclass.instance_eval &block
  end
end
module Defaults
  def self.included(klass, defaults = [])
    klass.metaclass_eval do
      define_method(:add_default) do |attr_name|
        # first, define getters and setters for the instances
        # i.e <class>.new.<attr_name> and <class>.new.<attr_name>=
        attr_accessor attr_name

        # open the class's class
        metaclass_eval do
          # now define our getter and setters for the class
          # i.e. <class>.<attr_name> and <class>.<attr_name>=
          attr_accessor attr_name
        end

        # add to our list of defaults
        defaults << attr_name
      end
      define_method(:inherited) do |subclass|
        # make sure any defaults added to the child are stored with the child
        # not with the parent
        Defaults.included( subclass, defaults.dup )
        defaults.each do |attr_name|
          # copy the parent's current default values
          subclass.instance_variable_set "@#{attr_name}", self.send(attr_name)
        end
      end
    end
    klass.class_eval do
      # define an initialize method that grabs the defaults from the class to 
      # set up the initial values for those attributes
      define_method(:initialize) do
        defaults.each do |attr_name|
          instance_variable_set "@#{attr_name}", self.class.send(attr_name)
        end
      end
    end
  end
end
class Foo
  include Defaults

  add_default :color
  # you can use the setter
  # (without `self.` it would think `color` was a local variable, 
  # not an instance method)
  self.color = "green"

  add_default :is_spherical
  # or the class instance variable directly
  @is_spherical = true
end

Foo.color #=> "green"
foo1 = Foo.new

Foo.color = "blue"
Foo.color #=> "blue"
foo2 = Foo.new

foo1.color #=> "green"
foo2.color #=> "blue"

class Bar < Foo
  add_defaults :texture
  @texture = "rough"

  # be sure to call the original initialize when overwriting it
  alias :load_defaults :initialize
  def initialize
    load_defaults
    @color = += " (default value)"
  end
end

Bar.color #=> "blue"
Bar.texture #=> "rough"
Bar.new.color #=> "blue (default value)"

Bar.color = "red"
Bar.color #=> "red"
Foo.color #=> "blue"
于 2010-01-28T16:59:49.333 に答える
2

これを行うこともできます:

def self.color your_args; your_expression end

define_method :color, &method(:color)
于 2010-12-09T10:23:45.370 に答える
1

パススルー機能を定義できます。

module Passthrough
  def passthrough(*methods)
    methods.each do |method|
      ## make sure the argument is the right type.
      raise ArgumentError if ! method.is_a?(Symbol)
      method_str = method.to_s
      self.class_eval("def #{method_str}(*args) ; self.class.#{method_str}(*args) ; end")
    end
  end
end

class Foo
  extend Passthrough

  def self::colour ; "green" ; end
  def self::is_spherical? ; true ; end
  passthrough :colour, :is_spherical?
end

f = Foo.new
puts(f.colour)
puts(Foo.colour)

私は一般的に を使用するのは好きではありませんevalが、ここではかなり安全なはずです。

于 2010-01-28T17:01:56.240 に答える
1

これはちょっと面倒なことのように聞こえるかもしれませんが、実際には、Foo.color を同じように簡単に呼び出すことができる場合、これを行う必要はほとんどありません。例外は、カラー メソッドが定義されたクラスが多数ある場合です。@var はいくつかのクラスの 1 つかもしれませんが、関係なく色を表示したいとします。

その場合、メソッドをどこで使用しているのかを自問したいと思います - クラス上ですか、それともモデル上ですか? ほとんどの場合、どちらか一方であり、すべてのインスタンスで同じであることが期待されていても、インスタンス メソッドにすることには何の問題もありません。

まれに、両方でメソッドを「呼び出し可能」にしたい場合は、 @var.class.color (特別なメソッドを作成せずに) を実行するか、次のような特別なメソッドを作成できます。

def color self.class.color end

すべてをキャッチする (method_missing) ソリューションは絶対に避けたいと思います。なぜなら、各メソッドの使用法や、それがクラス レベルまたはインスタンス レベルに属するかどうかを実際に考慮する必要がなくなるからです。

于 2010-01-28T16:51:11.490 に答える