【问题标题】:how to DRY this code?如何干燥这段代码?
【发布时间】:2013-09-16 23:28:24
【问题描述】:

我的几个模型中有以下代码行:

def average(scores)
  # get average of scores and round to two decimal places
  average = scores.inject{ |sum, el| sum + el }.to_f / scores.size
  average.round(2)
end

我尝试将它放入各种帮助文件中,并取得了不同程度的成功 - 但问题不是我无法工作,而是它需要一些丑陋的代码和/或额外的文件(模块等)只是在所有模型中都包含这种方法——这会引发一些危险信号。它不应该那么难。

Helper 代码对于控制器和视图来说很容易,但对于模型来说似乎真的违反直觉——同时,在 4 个地方(字面上)拥有完全相同的代码似乎很愚蠢。把它弄干的最好方法是什么?

更新

我想在每个模型的方法中使用 average 帮助器 - 在每种情况下都是不同的,但对于所有内容的最后一行 - 就像这样:

def avg_for(student)
  scores = []
  self.evals.map do |student_id, evals|
    evals.select {student_id == student.id}.each do |eval|
      scores << eval.score
    end  
  end    
  average(scores) #here!
end

【问题讨论】:

  • 你能提供更多的代码吗?助手仅用于视图,而不是控制器 + 模型。
  • @MichaelKoper,见编辑
  • model methods 是什么意思? instance methods?
  • 哦,抱歉,不是很清楚 - 已编辑
  • @dax 不确定我完全理解您的问题,但请查看我的编辑,我重写了您的 avg_for 方法。这就是你要找的吗?

标签: ruby-on-rails ruby-on-rails-3 dry helper


【解决方案1】:

http://api.rubyonrails.org/classes/ActiveRecord/Calculations.html#method-i-average

class Student < ActiveRecord::Base
  has_many :evals

  def average_score
    evals.average(:score)
  end
end

在 Rails 之外:

def average(score)
  (score.inject(:+).to_f / score.size).round(2)
end

编辑

使用您的avg_for 方法:

def avg_for(student)
  evals.where(student: student).average(:score)
end

【讨论】:

    【解决方案2】:

    对于这种非常具体的方法,您可以使用@delba 回答。

    要准确回答您关于跨模型共享方法的问题,这是concern 的工作。

    在 rails-4 中,关注点成为顶级公民,并自动创建目录 app/models/concernsapp/controllers/concerns

    您可以在app/concerns/averageable.rb 中添加类似的内容:

    module Averageable
      def average(scores)
        # get average of scores and round to two decimal places
        average = scores.inject{ |sum, el| sum + el }.to_f / scores.size
        average.round(2)
      end
    end
    

    然后,在你的模型中使用它:

    class User < ActiveRecord::Base
      include Averageable
    end
    

    您关注的方法将可用于包含它的任何模型。

    编辑:

    要在 rails-3 中执行相同的操作,请将您想要关注的路径添加到 config.autoload_paths 中,在 config/application.rb 中:

    config.autoload_paths += %W(#{config.root}/lib/concerns)
    

    并将averageable.rb 模块放在该目录中。

    【讨论】:

    • 您也可以在 rails-3 中执行此操作,您只需将模块放在可以自动加载的某个位置,例如 lib/。除此之外,它完全一样。
    • 获取uninitialized constant Goal::Averageable (NameError)(在Goal.rb
    • 你重启你的控制台/服务器/宙斯进程了吗?
    • 哦,等等,lib/ 在 rails-3 中也没有添加到 autoload_path 中。您必须将它(或您喜欢的任何目录)添加到config/application.rbconfig.autoload_paths 数组(默认可用注释)
    • 太棒了,我把 averageable.rb 直接放在 lib 中,所以我的 application.rb 添加看起来像这样:config.autoload_paths += %W(#{config.root}/lib)
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-09-03
    • 1970-01-01
    • 2015-08-18
    相关资源
    最近更新 更多