2012-03-11 15 views
3

私は他の通貨に$を変換するためのmethod_missingを実装しようとしています.5ドルの利回り5、5.yenは0.065 5.euro 6.56などとなります。これは私が今できることです。今私はそれを実装する必要がありますが、例えば5.dollars.in(:円)をしています。Ruby - method_missing

これは私が今持っているものです。

class Numeric 
    @@currencies = {'yen' => 0.013, 'euro' => 1.292, 'rupee' => 0.019} 
    def method_missing(method_id) 
    singular_currency = method_id.to_s.gsub(/s$/, '') 
    if @@currencies.has_key?(singular_currency) 
     self * @@currencies[singular_currency] 
    else 
     super 
    end 
    end 
end 

誰もが、私はこれを行うことができますどのように説明できますか?

PS:私はあなたにコードを渡すのではなく、説明をしたいので、私はそれがどのように行われたかを自分で判断することができます。

+1

を定義することができ、私は同じに取り組んでいます問題は、興味深いことに、この投稿は現在Googleのトップにあります"ruby method_missing"に対して10ヒット。 – cori

答えて

4

おそらくもっと役立つでしょう。それは(ノート、私はあなたがactivesupportの[Railsのの一部]とRubyを1.9.2+持っていることを期待しています)作業例です:

require 'rubygems' 

# This is allowing us to do the `pluralize` calls below 
require 'active_support/inflector' 

module Currency 
    CONVERSION_TABLE = { dollars: { dollars: 1, euros: 0.75 }, euros: { dollars: 1.3333334, euros: 1 } }.freeze 
    attr_accessor :currency 

    def method_missing(method_name, *args, &block) 
    # standardize on pluralized currency names internally so both singular 
    # and plural methods are handled 
    method_name = method_name.to_s.pluralize.to_sym 

    # Use the "from" keys in the conversion table to verify this is a valid 
    # source currency 
    if CONVERSION_TABLE.key?(method_name) 
     @currency = method_name 
     self # return self so a call to `1.dollar` returns `1` and not `:dollars` 
    else 
     super 
    end 
    end 

    # Convert `self` from type of `@currency` to type of `destination_currency`, mark the result with 
    # the appropriate currency type, and return. Example: 
    def to(destination_currency) 
    # Again, standardize on plural currency names internally 
    destination_currency = destination_currency.to_s.pluralize.to_sym 

    # Do some sanity checking 
    raise UnspecifiedSourceCurrency unless defined?(@currency) 
    raise UnsupportedDestinationCurrency unless CONVERSION_TABLE.key?(destination_currency) 

    # Do the actual conversion, and round for sanity, though a better 
    # option would be to use BigDecimal which is more suited to handling money 
    result = (self * CONVERSION_TABLE[@currency][destination_currency]).round(2) 

    # note that this is setting @currency through the accessor that 
    # was created by calling `attr_accessor :currency` above 
    result.currency = destination_currency 
    result 
    end 
end 

class Numeric 
    # Take all the functionality from Currency and mix it into Numeric 
    # 
    # Normally this would help us encapsulate, but right now it's just making 
    # for cleaner reading. My original example contained more encapsulation 
    # that avoided littering the Numeric clas, but it's harder for a beginner 
    # to understand. For now, just start here and you will learn more later. 
    include Currency 
end 

p 5.euros.to(:dollars)    #=> 6.67 
p 0.25.dollars.to(:euro)    #=> 0.19 
p 1.dollar.to(:euros).to(:dollar)  #=> 1.0 
+0

ありがとう、コアy – 8vius

2

selfにシンボルパラメータを送り返したinというメソッドを定義していませんか?あなたは量が現在何を表すのかわからないので

irb(main):057:0> 5.dollar.in(:euro) 
=> 6.46 
irb(main):065:0> 5.euro.in(:dollar) 
=> 6.46 # Which is wrong, by the way 

はそうではなく、かなり、 - あなたのmethod_missingはすべてがそれがない場合であっても、ドルであると仮定します。 money gemがありますなぜ:)

+0

これはスタンフォードのSaaSコースの宿題なので、私はそれをやることを学んでいるのですが、あなたは詳しく説明できますか? – 8vius

+0

@ 8vius '5.euro'は数値を返しますが、戻り値は単に別の' FixNum'です。それが表す通貨を知る方法がありません。すべての数字がドルを表していると仮定すると、数字を返すという考えは失敗します。これは、method_missingが何かをしようとしていたことを意味するためです。だから、あなたは他の答えが示唆しているものに近い何かをすることができ、 'in'関数はシンボルに対して単純な検索をして変換係数を得ることができます。 –

1

よりもむしろここmethod_missingを使用して、通貨のそれぞれを反復処理し、それらをあなたの変換方法に委任するための単数形と複数のメソッドを定義することが容易になるだろうだ

ここでは、便宜上ActiveSupportをお持ちであることを前提としています。あなたはこれを使わずに何でもすることができますが、constantizeのようなものは心配です。

module DavesMoney 
    class BaseMoney 
    # your implementation 
    end 

    class DollarConverter < BaseMoney 
    def initialize(value) 
     @value = value 
    end 

    def to(:currency) 
     # implemented in `BaseMoney` that gets extended (or included) 
    end 
    end 
end 

module CurrencyExtension 
    extend ActiveSupport::Concern 

    SUPPORTED_CURRENCIES = %w{ dollar yen euro rupee } 

    included do 
    SUPPORTED_CURRENCIES.each do |currency| 
     define_method :"#{currency}" do 
     return "#{currency}_converter".constantize.new(self) 
     end 
     alias :"#{currency.pluralize}" :"#{currency}" 
    end 
    end 
end 

# extension 
class Numeric 
    include CurrencyExtension 
end 
+0

コメントを読む私はDave up to topを出ました。 – 8vius

+0

いいえ、宿題は、Rubyのメタプログラミングとmethod_missingがどのように機能するのかを学ぶことです。彼らがこのようにするのは無関係です。 – 8vius

+2

私がここで実証したのは、はるかにパフォーマンスが良く、自明のメタプログラミングです。これは、通貨換算表を動的に調べていない限り、 'method_missing'の使用方法を学ぶ良い例ではありません。 Rubyのメタプログラミングについて実際に学びたいのであれば、[Paolo PerrottaによるRubyのメタプログラミング](http://www.amazon.com/Metaprogramming-Ruby-Program-Like-Pros/dp/1934356476)のコピーを入手してください。 – coreyward

9

を追加しまし通貨 'ドル' と方法で

class Numeric 
    @@currencies = {'dollar' => 1, 'yen' => 0.013, 'euro' => 1.292, 'rupee' => 0.019} 
    def method_missing(method_id) 
    singular_currency = method_id.to_s.gsub(/s$/, '') 
    if @@currencies.has_key?(singular_currency) 
     self * @@currencies[singular_currency] 
    else 
     super 
    end 
    end 

    def in(currency) 
    singular_currency = currency.to_s.gsub(/s$/, '') 
    self/@@currencies[singular_currency] 
    end 
end 
1

私のアプローチは、問題の限界を受け入れることに基づいています(Numericでmethod_missing実装を拡張しましたが、これは実際には何かのための間違ったアプローチであると示しています)。ではなく、宿題に問題があります。

をに翻訳することができることを理解し

:基本的に何が起こっている

eur = 5.send(:euros) 
eur.send(:in, yen) 

は、我々は数値5にユーロメッセージを送信し、その後パラメータで5.eurosの数値結果にin方法を送っているということですof:円。

eurosコールに応答してドルの換算結果を返すと、inコールに応答して(前回のコールから)inコールに応答してください(method_missingでも) inコールへのパラメータとして渡されたシンボルに変換します。それは適切な値を返します。

もちろん、換算係数が正しい限り、どのような通貨にでも変換することができます。この特定の問題のためには、ドルから/への変換が最も賢明だったようです。

1

私もこのコースをやっており、このタスクを達成する方法のいくつかの例を見ました。いくつかの点でself.sendを挙げると、私は他の誰かがまた、これを実現したと考えているが、私はこのソリューションは私のために働くことがわかった。

https://gist.github.com/2065412

3

これは、計算よりも多くの数学の問題です。

@@currenciesハッシュ値のそれぞれは、「ドル」に正規化される:その単位は円/ドルユーロ/ドルルピー/ドルあります。 5.euro.in(:yen)については、ユーロ/ドル円/ドルで割ります。円でユーロとして答えを表現するだけです。

Rubyを使用してこれを計算するには、method_missingメソッドを変更せずに、クラス定数を'dollar' => 1に更新します。この問題を解決するために、1行計算でNumeric#inメソッドを追加します。その計算では、浮動小数点数に正しい順序で除算を適用する必要があります。

5.euro.in(:yen)例えば、5.euroが最初に計算されますがユーロ/ドルの単位を持っていることを覚えておいてください。次に来るの(:円)メソッドは、この番号の逆数に適用する必要があります。これにより、単位は円/ユーロで、希望の結果の逆数になります。ここで

0

は「

  • はmethod_missingメソッドに新しい引数を追加通貨への追加 " 'ドル' => 1を" ...私が何をしたか

    http://pastebin.com/DpE8VAH4

     
    
        class Numeric 
         @@currencies = {'yen' => 0.013, 'euro' => 1.292, 'rupee' => 0.019, 'dollar' => 1} 
         def method_missing(method, *arg) 
         singular_currency = method.to_s.gsub(/s$/,'') 
         if @@currencies.has_key?(singular_currency) 
          self * @@currencies[singular_currency] 
         else 
          super 
         end 
         end 
         def in(arg) 
         singular_currency = arg.to_s.gsub(/s$/,'') 
         if @@currencies.has_key?(singular_currency) 
          self * @@currencies[singular_currency] 
         end 
         end 
        end 
    
        puts "5.euro = "+5.euro.to_s 
        puts "5.euros = "+5.euros.to_s 
        puts "5.dollars.in(:euros) = "+5.dollars.in(:euros).to_s 
        puts "10.euros.in(:rupees) = "+10.euros.in(:rupees).to_s 
    
    • あり、 * args "
    • 数値クラスに新しいメソッド" in(arg) "を追加
    • このメソッドは、引数 "arg"で指定された通貨
  • 0

    まず、ユニットライブラリ:gem install syをインストールします。次に、定義:

    require 'sy' 
    Money = SY::Quantity.dimensionless  #=> #<Quantity:Money> 
    USD = SY::Unit.standard of: Money  #=> #<Unit:USD of Money > 
    YEN = SY::Unit.of Money, amount: 0.013 #=> #<Unit:YEN of Money > 
    EUR = SY::Unit.of Money, amount: 1.292 #=> #<Unit:EUR of Money > 
    INR = SY::Unit.of Money, amount: 0.019 #=> #<Unit:INR of Money > 
    

    そして今、あなたが計算できます。

    10 * 10.usd => #<Magnitude: 100 > 
    100.yen.in :usd #=> #<Magnitude: 1.3 > 
    1.eur + 1.usd #=> #<Magnitude: 2.29 > 
    

    をあなたはまた

    CENT = SY::Unit.of Money, amount: 0.01.usd 
    EUROCENT = SY::Unit.of Money, amount: 0.01.eur 
    

    そして

    12.usd + 90.cent #=> #<Magnitude: 12.9 > 
    
    関連する問題