如何使用Object.send

时间:2016-03-21 21:23:32

标签: ruby csv

我试图运行以下代码:

class RentLimit < ActiveRecord::Base
  def self.load_data
    rows = CSV.open("csvs/income_limits_2011_to_2015.csv").read
    rows.shift
    rows.each do |county, yr, date, _50pct_1br, _50pct_2br, _50pct_3br, _50pct_4br, _60pct_1br, _60pct_2br, _60pct_3br, _60pct_4br|
      [50, 60].each do |ami|
        [1, 2, 3, 4].each do |br|
          r = new
          r.county = county
          r.state = "SC"
          r.year = yr
          r.effective_date = Date.parse(date)
          r.pct_ami = ami
          r.br = br
          r.max_rent = self.send("_#{ami}pct_#{br}br".to_sym)
          r.save
        end#of brs
      end# of amis
    end# of rows
  end
end

但在尝试运行时收到此错误消息:

NoMethodError: undefined method `_50pct_1br' for #<Class:0x007fe942ce3b18>

send方法无法访问范围内的那些块参数。有没有办法授予send块参数的访问权限?如果没有,我还可以动态访问块参数吗?

如何使用send或其等价物来访问Ruby中的块参数?

1 个答案:

答案 0 :(得分:0)

如果您告诉CSV.open列名是什么,这会容易得多。看起来您的CSV文件可能有一个标题行,您正在使用rows.shift跳过,在这种情况下,您不应跳过它,并使用headers: true选项。然后,您可以使用row["field_name"]按名称访问每个字段,或者根据您的情况row["_#{ami}pct_#{br}br"]

CSV_PATH = "csvs/income_limits_2011_to_2015.csv"

DEFAULT_STATE = "SC"

def self.load_data
  CSV.open(CSV_PATH, 'r', headers: true) do |csv|
    csv.each do |row|
      max_rent = row["_#{ami}pct_#{br}br"]

      create(
        county: row["county"],
        state: DEFAULT_STATE,
        year: row["yr"],
        effective_date: Date.parse(row["date"]),
        pct_ami: ami,
        br: br,
        max_rent: max_rent,
      )
    end
  end
end

请注意,我将CSV.open与块一起使用,以确保文件在读取后关闭,而原始代码没有这样做。我还使用了create而不是new; ... save,因为后者是不必要的冗长。

如果由于其他原因而跳过第一行,或者您想使用标题行以外的字段名称,则可以设置选项return_headers: false, headers: names,其中names是名字数组,例如:

CSV_HEADERS = %w[
  county yr date _50pct_1br _50pct_2br _50pct_3br _50pct_4br
  _60pct_1br _60pct_2br _60pct_3br _60pct_4br
].freeze

def self.load_data
  CSV.open(CSV_PATH, 'r', return_headers: false, headers: CSV_HEADERS) do |csv|
    # ...
  end
end

最后,由于您创建的每个对象的某些属性都相同,我会将它们移出循环:

def self.load_data
  base_attrs = { state: DEFAULT_STATE, pct_ami: ami, br: br }

  CSV.open(CSV_PATH, 'r', headers: true) do |csv|
    csv.each do |row|
      create(base_attrs.merge(
        county: row["county"],
        year: row["yr"],
        effective_date: row["date"],
        max_rent: row["_#{ami}pct_#{br}br"]
      ))
    end
  end
end