【问题标题】:Including .xml file to rails and using it将 .xml 文件包含到 rails 并使用它
【发布时间】:2013-10-28 17:09:47
【问题描述】:

所以我有这个货币 .xml 文件:

http://www.ecb.int/stats/eurofxref/eurofxref-daily.xml

现在,我想知道如何让我的 rails 应用程序读取它?我什至必须把它放在哪里以及如何包含它?
我基本上是在做一个汇率计算器。

我要让下拉菜单中出现 .xml 表中的货币名称并且可以使用。

【问题讨论】:

    标签: ruby-on-rails xml


    【解决方案1】:

    首先,您必须能够读取该文件——我假设您想要该站点的最新版本,因此您将发出 HTTP 请求(否则,只需将文件存储在您的应用程序并使用File.read 使用相对路径阅读它)。这里我使用Net::HTTP,但你可以使用HTTParty 或任何你喜欢的。

    它看起来每天都在变化,所以也许您只想每天发出一个 HTTP 请求并将文件与时间戳一起缓存在某处。

    假设您的应用程序中有一个名为 rates 的目录,我们在其中存储缓存的 xml 文件,功能的核心可能如下所示(有点笨拙,但我希望行为显而易见):

    def get_rates
      today_path = Rails.root.join 'rates', "#{Date.today.to_s}.xml"
    
      xml_content = if File.exists? today_path
                      # Read it from local storage
                      File.read today_path
                    else
                      # Go get it and store it!
                      xml = Net::HTTP.get URI 'http://www.ecb.europa.eu/stats/eurofxref/eurofxref-daily.xml'
                      File.write today_path, xml
                      xml
                    end
    
      # Now convert that XML to a hash. Lots of ways to do this, but this is very simple xml.
      currency_list = Hash.from_xml(xml_content)["Envelope"]["Cube"]["Cube"]["Cube"]
    
      # Now currency_list is an Array of hashes e.g. [{"currency"=>"USD", "rate"=>"1.3784"}, ...]
      # Let's say you want a single hash like "USD" => "1.3784", you could do a conversion like this
      Hash[currency_list.map &:values]
    end
    

    重要的部分是Hash.from_xml。如果您拥有本质上是键/值对的 XML,那么这就是您的朋友。对于更复杂的事情,您将需要寻找像 Nokogiri 这样的 XML 库。 ["Envelope"]["Cube"]["Cube"]["Cube"] 正在挖掘哈希以到达重要部分。

    现在,您可以看到这对 XML 结构中的任何更改有多敏感,您应该使端点可配置,并且该哈希可能足够小以缓存在内存中,但是这是基本思想。

    要从哈希中获取您的货币列表,只需说get_rates.keys

    只要你明白发生了什么,你就可以把它变小:

    def get_rates
      today_path = Rails.root.join 'rates', "#{Date.today.to_s}.xml"
    
      Hash[Hash.from_xml(if File.exists? today_path
                           File.read today_path
                         else
                           xml = Net::HTTP.get URI 'http://www.ecb.europa.eu/stats/eurofxref/eurofxref-daily.xml'
                           File.write today_path, xml
                           xml
                         end)["Envelope"]["Cube"]["Cube"]["Cube"].map &:values]
    end
    

    如果您选择缓存 xml,您可能还希望自动清除缓存的 XML 文件的旧版本。如果要缓存其他转换列表,请考虑从 URI 自动派生的命名方案,例如eurofxref-daily-2013-10-28.xml

    编辑:假设您想将转换后的 xml 缓存在内存中——为什么不呢!

    module CurrencyRetrieval
      def get_rates
        if defined?(@@rates_retrieved) && (@@rates_retrieved == Date.today)
          @@rates
        else
          @@rates_retrieved = Date.today
          @@rates = Hash[Hash.from_xml(Net::HTTP.get URI 'http://www.ecb.europa.eu/stats/eurofxref/eurofxref-daily.xml')["Envelope"]["Cube"]["Cube"]["Cube"].map &:values]
        end
      end
    end
    

    现在只需include CurrencyRetrieval,无论您在哪里需要它,您都是金子。 @@rates@@rates_retrieved 将作为类变量存储在您包含此模块的任何类中。您必须测试这在生产设置中的调用之间是否存在(否则回退到基于文件的方法或将这些值存储在其他地方)。

    请注意,如果 XML 结构发生变化,或者 XML 今天不可用,您需要使 @@rates 无效并以某种不错的方式处理异常...安全总比后悔好。

    【讨论】:

    • 我现在可以仅使用currency_list 调用货币吗?我仍在使用您给出的第一个示例,试图研究它:) 我的意思是.. 我可以在 select_tag 中使用它吗?目前我有这条线: options_for_select(@currency_list, :currency)
    • 但这给了我一个错误:undefined method `map' for nil:NilClass 以前从未使用过 select_tag,习惯了。但我们的想法是让货币名称出现在其中。
    • 另外,为什么要在一个模块中使用两个@?
    • @@ 前缀使变量成为类变量。这意味着它们将被设置在类级别——而不是实例级别。因此,假设您在 ApplicationController 中使用 include CurrencyRetrieval,这意味着从 ApplicationController 继承的任何内容的每个实例(可能是您的所有控制器)都可以访问相同的缓存列表。
    • 我不完全明白你的意思(如果这是在客户端或查看代码)最好只用这个提出第二个问题。如果您有货币名称到符号的映射,则使用当前选项的文本查找该映射应该很简单(不是值,因为这将是该货币的转换率,例如 1.317 而不是“美元”)。当您构建该选择时,您可以将其编码为选项的 HTML 属性——如果您查找文档,options_for_select 非常灵活。
    猜你喜欢
    • 1970-01-01
    • 2020-09-08
    • 2013-10-09
    • 2011-09-23
    • 1970-01-01
    • 1970-01-01
    • 2011-08-30
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多