Question

Je suis en train de mettre en œuvre un method_missing pour la conversion $ à d'autres monnaies, comme en fait 5.dollars des rendements de 5, 5.yen aurait rendement de 0,065 5.euro 6.56 et ainsi de suite.Ce que je peux faire maintenant.Maintenant, j'ai besoin de le mettre en œuvre mais faire 5.de dollars.dans(:yen) par exemple.

C'est ce que j'ai en ce moment:

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

Quelqu'un peut-il expliquer comment je peux faire cela?

PS:Je préfèrerais que vous me donnez pas le code, mais une explication, afin que je puisse déterminer sur mon propre comment il est fait.

Était-ce utile?

La solution

Perhaps this will be of more help. It's a working example (note, I'm expecting you to have ActiveSupport [part of Rails] and 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

Autres conseils

monnaie ajoutée 'dollar' et dans méthode:

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

Ce n'est plus un problème mathématique de calcul un.

Chacun des @@currencies les valeurs de hachage est normalisé à 'dollars':leurs unités sont yen/dollar, euro/dollar, roupie/dollar.Pour 5.euro.in(:yen), vous avez seulement besoin de diviser euro/dollar par yen/dollar pour exprimer la réponse en euro, en Yen.

Pour le calcul de ce à l'aide de Ruby, vous quittez le method_missing méthode inchangée et mise à jour de la constante de classe pour inclure 'dollar' => 1.Ajouter un Numeric#in méthode avec une seule ligne de calcul pour résoudre ce problème.Que le calcul doit s'appliquer à la division dans la séquence correcte d'un nombre à virgule flottante.

Pour 5.euro.in(:yen) exemple, rappelez-vous que 5.euro est calculé d'abord, mais qui aura des unités de euro/dollar.L' dans(:yen) la méthode qui vient suivant doit être appliqué à l'inverse de ce nombre.Cela vous donnera un certain nombre d'unités dans yen/euro, la réciproque de votre résultat souhaité.

Ne serait-il pas de définir une méthode appelée in qui a envoyé le symbole de paramètre de retour à la self?

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

Donc, pas tout à fait, parce que vous ne savez pas ce que le montant représente actuellement--votre method_missing suppose que tout est en dollars, même si elle ne l'est pas.

C'est pourquoi il y a la l'argent gem :)

Plutôt que d'utiliser method_missing ici, il serait plus facile de faire une itération sur chaque devises et de définir le singulier et le pluriel de méthodes de déléguer à votre méthode de conversion.

Je suis en supposant que vous avez ActiveSupport ici par souci de commodité.Vous pourriez faire tout cela sans, mais des choses comme constantize et les préoccupations de la rendre plus facile.

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

Mon approche, basée sur l'acceptation de la limite du problème posé (étendre un method_missing mise en œuvre sur le Numérique, même si comme @coreyward indique c'est vraiment pas la bonne approche pour rien pas un problème) est comme suit:

La compréhension que 5.euros.in(:yen) peut être traduite:

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

ce qui est essentiellement ce qui se passe est que nous allons envoyer le message d'euros pour le Numérique 5 et l'envoi du in méthode pour le résultat Numérique de 5.d'euros avec un paramètre de :yen.

Dans method_missing vous devez répondre à la euros d'appel et de retour avec la suite de l'une d'euros de dollars de la conversion, puis (également dans method_missing) pour répondre à la in d'appels avec les résultats de la conversion des dollars (de l'appel précédent) pour le symbole transmis comme paramètre à la in appel.Qui va renvoyer la valeur correcte.

Bien sûr, vous pouvez convertir vers/à partir de n'importe quelle devise vous voulez tant que vos facteurs de conversion sont corrects avec le givens pour ce problème particulier, la conversion vers/à partir de dollars semblait le plus sensible.

Je fais aussi ce cours et j'ai vu quelques exemples de la manière d'accomplir la tâche.À un moment donné, Self.Send a été mentionné et je crois que quelqu'un d'autre l'a mis en œuvre aussi mais j'ai trouvé cette solution pour travailler pour moi:

https://gist.github.com/2065412

Voici ce que j'ai fait ...

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

  • Ajouter "Dollar '=> 1" dans les devises
  • Ajoutez un nouvel argument dans la méthode_missing méthode ", * args"
  • Ajouter une nouvelle méthode "dans (arg)" dans la classe numérique
  • Cette méthode se multiplie par la monnaie spécifiée par l'argument "arg"

Tout d'abord, installer mes unités de la bibliothèque: gem install sy.Ensuite, définir:

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 >

Et maintenant vous pouvez calculer:

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

Vous pouvez également définir

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

Et puis

12.usd + 90.cent #=> #<Magnitude: 12.9 >
Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top