0

私のRubyアプリケーションでは、元のクラスに影響を与えずにクローンに若干の変更を加えることができるように、クラスのクローンを作成したいと思います(詳細については以下の注を参照してください)。残念ながら、複製されたクラスは私が期待するように動作していません。具体的には、複製されたクラスのクラスメソッドは、定数とクラス変数にアクセスするのに問題があるようです。観察:

irb(main):001:0> class Foo
irb(main):002:1>   HELLO = "Hello, world!"
irb(main):003:1>   def self.say_hello
irb(main):004:2>     HELLO
irb(main):005:2>   end
irb(main):006:1>   def self.cls_var=(val)
irb(main):007:2>     @@cls_var = val
irb(main):008:2>   end
irb(main):009:1>   def self.cls_var
irb(main):010:2>     @@cls_var
irb(main):011:2>   end
irb(main):012:1> end
=> nil
irb(main):013:0> Foo.say_hello
=> "Hello, world!"
irb(main):014:0> Foo.cls_var = "Test"
=> "Test"
irb(main):015:0> Foo.cls_var
=> "Test"
irb(main):016:0> Bar = Foo.clone
=> Bar
irb(main):017:0> Bar.say_hello
NameError: uninitialized constant Class::HELLO          # ???
        from (irb):4:in `say_hello`
        from (irb):17
        from C:/Ruby193/bin/irb:12:in `<main>`
irb(main):018:0> Bar.cls_var = "Another test"
(irb):7: warning: class variable access from toplevel   # Say what?
=> "Another test"
irb(main):019:0> Bar.cls_var
(irb):10: warning: class variable access from toplevel
=> "Another test"
irb(main):020:0> Foo.cls_var
=> "Another test"                                       # Why???

ここで何が起こっているのでしょうか。また、クローンを作成した後、BarがFooとまったく同じように機能するように、これを修正するにはどうすればよいですか。

注:この質問は、Rubyのフォローアップです。継承されたメソッドが古い定数ではなく新しい定数を使用するように、サブクラスの定数を「オーバーライド」する方法はありますか?


更新:申し訳ありませんが、なぜこれをやりたいのかはっきりしていなかったと思います。したがって、私の場合Fooは、クラスの1つに必要な機能とほぼ同じ機能を持つgemのクラスです。Foo実際、私が欲しいものとの唯一の違いは、その厄介なHELLO定数です。MyClass.say_hello「こんにちは、ボブ!」を返したいです。「Hello、World!」の代わりに。(そして、オーバーライドすることを提案する前にsay_hello、私の場合、Fooには他にも多くのメソッドがHELLOありsay_hello、私の例よりもはるかに複雑です。)

今、私はで変更Foo::HELLOすることができますFoo::HELLO.slice!(0, 7) << "Bob!"が、それは私が望まない宝石の振る舞いを変更します。Fooでは、の値が異なる正確な複製を作成するにはどうすればよいでしょうHELLOか。

TLDR: Fooはgemの一部なので、ソースを編集したくありません。HELLO別の値に設定されていることを除いて、Fooとまったく同じように動作するクラスが必要です。

4

5 に答える 5

2

クローンの後に定数がリストされていることに気づきました。Foo.constantsBar.constants両方が表示され[:HELLO]ます。

selfクラスメソッドへの追加は機能しているようです。

 class Foo
   HELLO = "Hi"
   def self.say_hello
     self::HELLO
   end
 end
于 2012-11-05T16:01:29.373 に答える
1

Rubyでのクラスのクローン作成のセマンティクスは、思ったとおりに機能しません。クラスメソッドを使用すると、定数の問題を回避できます。

class Foo
  def self.say_hello
    "Hello, world!"
  end
end

(または、@ MichaelDodgeの回答を使用してください。)

クラス変数を使用して、複製されたクラス間で値を共有することはできません。値を共有したい正当な理由がある場合は、他のメカニズムを使用して共有する必要があります。

あなたのコメントの1つで、クラスのクローンを作成して変更する理由は、それらがgemの一部であるためであると述べました。その場合は、gemをフォークして、必要に応じてソースを変更してみませんか?

于 2012-11-05T15:51:19.543 に答える
0

なぜ単純にサブクラス化しないのFooですか?

class Foo
  HELLO = 'hi'
end

Foo::HELLO
#=> "hi"

class Bar < Foo; end

Bar::HELLO
#=> "hi"

class Bar < Foo
  HELLO = 'hello there'
end

Bar::HELLO
#=> "hello there"
于 2012-11-05T15:42:53.553 に答える
0

したがって、最初に元のクラスに設定してから、コードの他の部分を傷つけないようにリセットして戻すことができます。

class Foo
  HELLO = "Hello, world!"
  def self.say_hello
    HELLO
  end
  def self.cls_var=(val)
    @@cls_var = val
  end
  def self.cls_var
    @@cls_var
  end

end

スクリプト内:

#set new hello
class Foo
  OLD_HELLO = HELLO
  HELLO = "NEW HELLO WORLD"
end

class Bar < Foo
end

Bar.say_hello
#output: => "NEW HELLO WORLD"

#reset hello back
class Foo
  HELLO = OLD_HELLO
end
于 2012-11-05T17:42:52.457 に答える
0

したがって、ここでのコンセンサスは、このコンテキストで期待するようにクローンを機能させる簡単な方法はないということのようです。代替ソリューションはたくさんありますが、期待どおりcloneに機能するものはありません。


まず、元のクラスを編集して、次self::HELLOの代わりに参照することで、定数の問題を修正することができHELLOます。

# Before:
class Foo
  HELLO = "Hello, world!"
  def self.say_hello
    HELLO
  end
end
Bar = Foo.clone
Bar.say_hello # Error

# After:
class Foo
  HELLO = "Hello, World!"
  def self.say_hello
    self::HELLO
  end
end
Bar = Foo.clone
Bar.say_hello # => "Hello, world!"

残念ながら、このソリューションではクラス変数の問題は解決されないため、Fooのソースを編集する必要があります。これは、Fooがgemまたはその他の外部ライブラリの一部である場合は望ましくない場合があります。


別の解決策は、クローンを作成する代わりにFooをサブクラス化することです。

class Foo
  HELLO = "Hello, world!"
  def self.say_hello
    HELLO
  end
end

class Bar < Foo
end
Bar.say_hello # => "Hello, world!"

Bar.say_helloこれに伴う問題は、複製されたクラスから予想されるように、Bar::HELLOを再定義してもの結果に影響を与えないことです。

Bar.const_set :HELLO, "Hello, Bar!"
Bar.say_hello # => "Hello, world!"

全体として、最も効果的な解決策は、おそらくのソースコードFooを別のクラスに手動でコピーすることです。これは動的ではありませんが、結果はあなたが期待するものとまったく同じですclone

于 2012-11-09T19:05:29.083 に答える