无法从其他文件调用数据

时间:2018-06-29 13:05:45

标签: json ruby

我知道这很简单,但是我很难从JSon数据文件中获得想要的值。

我有两个文件,一个叫做test.rb,看起来像这样:

require_relative 'suppliers_data' 

class SelectSupplier
  def self.suppliers
    suppliers.sort_by { |e| e[:advitam_grade].to_i }.reverse
  end
end

和另一个名为Suppliers_data的样子:

suppliers = [
{ name: "FunePlus",
  advitam_grade: 3,
  works: [
    { type: "embalming", price: 350 },
    {type: "transport_before_casketing", price: 450} ]},
{ name: "FuneTop",
  works: [
    { type: "graving", price: 10} ]},
{ name: "FuneTruc",
  advitam_grade: 5,
  works: [
    { type: "embalming", price: 750} ]},
{ name: "FuneCorp",
  advitam_grade: 2,
  works: [
    { type: "digging", price: 350} ]}

]

您可能会想像,我的def self.suppliers无法正常工作,并且出现此错误suppliers': stack level too deep

我试图环顾四周我做错了什么,但没有发现真正有趣的东西。

你们有没有解决办法?

谢谢。

3 个答案:

答案 0 :(得分:1)

此错误的原因是无限的递归

def self.suppliers
    suppliers. # calls itself recursively
      sort_by { |e| e[:advitam_grade].to_i }.reverse

end

答案 1 :(得分:0)

您所做的完全错误(ruby本机不支持将未序列化的对象从磁盘存储/加载到磁盘,或将其加载到磁盘,),但是它是ruby的,并且一切正常:

class SelectSupplier
  def self.suppliers
    instance_eval '@' << File.read('suppliers_data')
    @suppliers.sort_by { |e| e[:advitam_grade].to_i }.reverse
  end
end

此任务的正确解决方案是将数据序列化为JSON或YAML,加载文件并反序列化数据。

答案 2 :(得分:0)

发生“堆栈级别太深”异常的原因是SelectSupplier::suppliers调用自身而不是变量。这是因为变量超出范围。您可以通过以下两种方法之一解决此问题。

  1. 使用常量定义。

    要将变量变成常量,必须大写名称。此外,常数通常是常数,就象名称所暗示的那样。为了防止更改,通常会冻结这些值。由于常量的数据结构相对复杂,冻结过程非常复杂。通常,常量用于简单的事情,例如星期几或允许的选项。因此,我不建议使用常量。

    SUPPLIERS = [
      {
        name: 'FunePlus',
        advitam_grade: 3,
        works: [
          {type: 'embalming', price: 350},
          {type: 'transport_before_casketing', price: 450},
        ],
      }, # {
        # ...
      # }
    ].freeze.each do |supplier|
      supplier.freeze.each_value(&:freeze)
      supplier[:works].each(&:freeze).each { |work| work.each_value(&:freeze) }
    end
    

    然后在您的方法中使用SUPPLIERS而不是suppliers

  2. 使用全局变量。

    应避免使用全局变量,但这是一个选择。要创建全局变量,您必须在变量前加上$

    $supliers = [
      # ...
    ]
    

    然后在您的方法中使用$suppliers而不是suppliers

以上两种解决方案都不好,这是由于您的设计。如果您打算将这些值保存在内存中,则最好使用一个类。这样,您可以为实例添加一些自定义方法。以下内容可能会让您了解此类的外观:

class Supplier
  @suppliers = [
    # ...
  ]

  class << self
    attr_reader :suppliers

    # initialize an instance with an id
    def find(id)
      supplier = suppliers[id]

      return unless supplier

      new(id: id, **suppliers)
    end

    # initialize an instance with an attribute
    def find_by(attributes)
      supplier = suppliers.find do |supplier|
        attributes.all? { |attr, val| supplier[attr] == val }
      end

      return unless supplier

      new(id: suppliers.index(supplier), **supplier)
    end
  end

  # create getters for attributes
  attr_reader :id, :name, :advitam_grade, :works

  def initialize(attributes = {})
    @id = attributes.delete(:id)
    attributes.each { |attr, value| public_send("#{attr}=", value) }
    @initialized = true
  end

  # create setters that update the source hash if the instance is
  # initialized
  %i[name advitam_grade works].each do |symbol|
    define_method("#{symbol}=") do |value|
      instance_variable_set("@#{symbol}", value)
      # you can't use return in a block, so instead use break
      break unless @initialized
      update(symbol)
    end
  end

  # return the source hash
  def source
    klass[id]
  end

  private

  # update the source hash with the instance attribute value
  def update(attr)
    klass[id][attr] = public_send(attr)
  end

  def klass
    self.class
  end

end