重构这个Ruby和Rails代码

时间:2011-01-20 10:47:18

标签: ruby-on-rails ruby

我有这个型号:

class Event < Registration
  serialize :fields, Hash
  Activities=['Annonce', 'Butiksaktivitet', 'Salgskonkurrence']

  CUSTOM_FIELDS=[:activity, :description, :date_from, :date_to, :budget_pieces, :budget_amount, :actual_pieces, :actual_amount]
  attr_accessor *CUSTOM_FIELDS

  before_save :gather_fields
  after_find :distribute_fields

  private

  def gather_fields
    self.fields={}
    CUSTOM_FIELDS.each do |cf|
      self.fields[cf]=eval("self.#{cf.to_s}")
    end
  end

  def distribute_fields
    unless self.fields.nil?
      self.fields.each do |k,v|
        eval("self.#{k.to_s}=v") 
      end
    end
  end
end

我觉得这可以做得更短更优雅。有没有人有想法?

  • 雅各

顺便说一句。谁能告诉我CUSTOM_FIELDS前面的星号是什么?我知道它在方法定义中的作用(def foo(* args))但不在这里......

4 个答案:

答案 0 :(得分:3)

首先关闭:从不10000000000.times { puts "ever" }使用eval ,当您不知道自己在做什么时。它是红宝石世界的核弹,它可以在广泛的区域内造成破坏,在整个代码中引起辐射中毒的类似症状。只是不要。

考虑到这一点:

class Event < Registration
  serialize :fields, Hash
  Activities = ['Annonce', 'Butiksaktivitet', 'Salgskonkurrence']

  CUSTOM_FIELDS = [:activity, 
                 :description,
                 :date_from,
                 :date_to,
                 :budget_pieces,
                 :budget_amount,
                 :actual_pieces,
                 :actual_amount] #1
  attr_accessor *CUSTOM_FIELDS #2

  before_save :gather_fields
  after_find :distribute_fields

  private

  def gather_fields
    CUSTOM_FIELDS.each do |cf|
      self.fields[cf] = send(cf) #3
    end
  end

  def distribute_fields
    unless self.fields.empty?
      self.fields.each do |k,v|
        send("#{k.to_s}=", v) #3
      end
    end
  end
end

现在有一些注意事项:

  1. 通过将每个自定义字段放在自己的行上,可以提高代码的可读性。我不想滚动到行尾以读取所有可能的自定义字段或添加我自己的字段。
  2. 传入*CUSTOM_FIELDS的{​​{1}}使用了所谓的“splat运算符”。通过以这种方式调用它,attr_accessor数组的元素将作为单独的参数传递给CUSTOM_FIELDS方法而不是作为一个(数组本身)
  3. 最后,我们使用attr_accessor方法调用我们不知道编程期间名称的方法,而不是邪恶的send
  4. 除此之外,我找不到任何重构此代码的内容。

答案 1 :(得分:1)

我同意以前的海报。此外,我可能会将gather_fields和distribute_fields方法移动到父模型,以避免在每个子模型中重复代码。

class Registration < ActiveRecord::Base
  ...

protected
  def gather_fields(custom_fields)
    custom_fields.each do |cf|
      self.fields[cf] = send(cf)
    end
  end

  def distribute_fields
    unless self.fields.empty?
      self.fields.each do |k,v|
        send("#{k.to_s}=", v)
      end
    end
  end
end

class Event < Registration
  ...

  before_save :gather_fields
  after_find :distribute_fields

private
  def gather_fields(custom_fields = CUSTOM_FIELDS)
    super
  end
end

答案 2 :(得分:0)

你可以用发送电话取代两个人:

self.fields[cf] = self.send(cf.to_s)


self.send("#{k}=", v)

“#{}”执行to_s,因此您不需要k.to_s

作为一个常数的活动应该是活动。

答案 3 :(得分:0)

对于那个星号*,请查看此帖子:What is the splat/unary/asterisk operator useful for?

Activities=['Annonce', 'Butiksaktivitet', 'Salgskonkurrence']

可以写成:ACTIVITIES = %w(Annonce, Butiksaktivitet, Salgskonkurrence).freeze因为你要定义一个常量。

def distribute_fields
  unless self.fields.empty?
    self.fields.each do |k,v|
      send("#{k.to_s}=", v) #3
    end
  end
end

可以写成一个班轮:

def distribute_fields 
  self.fields.each { |k,v| send("#{k.to_s}=", v) } unless self.fields.empty?
end
Ryan Bigg给出了一个很好的答案。