我正在尝试实现一个用于将$转换为其他货币的method_missing,就像5.dollars产生5,5.yen将产生0.065 5.euro 6.56等等。我现在可以做。现在我需要实现它,但是例如5.dollars.in(:yen)。
这就是我现在所拥有的:
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:我宁愿你不给我代码,而是解释,所以我可以自己确定它是如何完成的。
答案 0 :(得分:10)
在方法中添加货币'美元'和:
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 :(得分: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
答案 2 :(得分:3)
这比计算问题更像是一个数学问题。
每个@@currencies
哈希值都标准化为“美元”:它们的单位是日元/美元,欧元/美元,卢比/元。对于5.euro.in(:yen)
,您只需要将欧元/美元除以 yen / dollar 来表示日元为欧元的答案。
要使用Ruby计算此值,请保持method_missing
方法不变,并更新类常量以包含'dollar' => 1
。使用单行计算添加Numeric#in
方法来解决此问题。该计算需要以正确的顺序将除法应用于浮点数。
对于5.euro.in(:yen)
示例,请记住首先计算 5.euro ,但其单位为 euro / dollar 。接下来的 in(:yen)方法必须应用于此数字的倒数。这将给出一个单位为 yen / euro 的数字,这是你想要的结果的倒数。
答案 3 :(得分:2)
您是不是只定义了一个名为in
的方法,该方法将符号参数发送回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
所以,不完全是因为你不知道目前的金额是多少 - 你的method_missing
假定一切都是美元,即使它不是。
这就是money gem:)
的原因答案 4 :(得分: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
答案 5 :(得分:1)
我的方法是基于接受所提出的问题的限制(在Numeric上扩展method_missing实现,即使@coreyward表明这对任何事情来说都是错误的方法不一个家庭作业问题)如下:
了解5.euros.in(:yen)
可以转换为:
eur = 5.send(:euros)
eur.send( :in, yen )
基本上发生的是我们将欧元消息发送到数字5,然后将in
方法发送到5.euros的数字结果,参数为:yen。
在method_missing中,您应该回复euros
来电,并返回欧元转换成美元的结果,然后(也在method_missing中)回复in
调用并转换结果美元(从前一次调用)到作为参数传递给in
调用的符号。这将返回正确的值。
当然,只要你的转换因子是正确的,你就可以兑换成你想要的任何货币 - 对于这个特殊问题,转换成美元/从美元转换似乎是最明智的。
答案 6 :(得分:1)
我也在做这个课程,我看到了一些如何完成任务的例子。在某些时候提到了self.send,我相信其他人也实现了这一点,但我发现这个解决方案对我有用:
答案 7 :(得分:0)
这就是我做的......
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
答案 8 :(得分: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 >