大量逻辑的正确导轨方法

时间:2015-04-10 11:56:40

标签: ruby-on-rails ruby ruby-on-rails-4 model-view-controller

我目前正在建立一个每周举办自主竞赛的网站。我的逻辑检查前一周是否有指定的获胜者,如果没有,它会滚动,找到胜利者并指定一个奖杯。

逻辑一切正常,但很多都是在应用程序控制器中运行,而在我的核心,我觉得这是不对的。

例如,如果第一名的票数高于第二名,而第二名的票数高于第三名,则需要创建第一名的第二名和第三名奖杯并将其奖励给正确的用户。

        if first_place > second_place && second_place > third_place
          @week_previous.winner_id  = week_entries[0].id
          @week_previous.save

          first_trophy = week_entries[0].user.trophies.new
          first_trophy.week_id  = @week_previous.id
          first_trophy.user_id  = week_entries[0].user_id
          first_trophy.position = "first"
          first_trophy.country  = week_entries[0].user.country 
          first_trophy.pro      = false
          first_trophy.save

          if second_place >= 1
            second_trophy = week_entries[1].user.trophies.new
            second_trophy.week_id  = @week_previous.id
            second_trophy.user_id  = week_entries[1].user_id
            second_trophy.position = "second"
            second_trophy.country  = week_entries[1].user.country 
            second_trophy.pro      = false
            second_trophy.save
          end

          if third_place >= 1
            third_trophy = week_entries[2].user.trophies.new
            third_trophy.week_id  = @week_previous.id
            third_trophy.user_id  = week_entries[2].user_id
            third_trophy.position = "third"
            third_trophy.country  = week_entries[2].user.country 
            third_trophy.pro      = false
            third_trophy.save
          end
        end

这是将Trophies直接构建到控制器中,我经常听到'胖模型瘦调控制器'的说法,我觉得我的运行方式与此完全相反!

我如何将奖杯创作转移到模型中?我确信我可以在周模型中使用类似after_save的东西,但我不完全确定如何保持逻辑运行。我有几次尝试,但我经常收到错误undefined method to_model

我知道我可以继续努力让它发挥作用,但我觉得这不是做事的'Rails方式',所以我想在早期阶段解决这个问题。

非常感谢任何帮助。

谢谢!

根据评论进行修改:

感谢您抽出宝贵时间来研究这个问题。在坚果壳中,我想要实现的是一个竞赛从周一到周日的系统。 “活动周”的范围限定为Date.today介于:start_date和:end_date之间的周。

接下来的星期一开始新的一周,这将活跃的一周移动到前一周,然后它将奖杯分配给前一周的前三个条目。它通过检查前一周是否有winner_id来分配奖杯。如果是这样,它不会运行任何逻辑,但是一旦范围移动到新的一周,前几周:winner_id现在为零,所以逻辑运行第一次有人在新的一周来到网站。

愚蠢地说:

  • 周是一个资源,其中包含多个条目。
  • 条目属于用户,属于一周,并且有很多投票。
  • 投票属于条目
  • 用户has_many条目和has_many Trophies
  • Trophy属于用户且属于一周

因此,用户在当前活动周上的一个条目上投票。一旦一周超出活动范围,它就会为位于本周前3位的用户创建奖杯。

2 个答案:

答案 0 :(得分:1)

如果不了解背景,很难给出好的建议。但引起我注意的是,该代码中有很多重复,并且您在重复中更新了用户奖杯。因此,作为第一步,我至少会将这种逻辑移到用户身上:

# in user.rb
def record_trophy_win(prev_week, entry, position)
  trophies.create(
    week_id:  prev_week.id,
    user_id:  entry.user_id,
    position: position,
    country:  entry.user.county,
    pro:      false 
  )
end

这允许将控制器中的部分更改为:

if first_place > second_place && second_place > third_place
  @week_previous.update_attribute(:winner_id, week_entries[0].id)

  first_trophy = week_entries[0].user.record_trophy_win(
    @week_previous, week_entries[0], 'first'
  )

  second_trophy = week_entries[1].user.record_trophy_win(
    @week_previous, week_entries[1], 'second'
  ) if second_place >= 1

  third_trophy = week_entries[2].user.record_trophy_win(
    @week_previous, week_entries[2], 'third'
  ) if third_place >= 1
end

该逻辑可能在下一步属于Trophy类。取决于背景......

我注意到您的week_entry有一个user。并且您添加了user trophy,其中还有一些user字段。这不会导致循环依赖吗?或者您是否使用完全相同的条目覆盖现有记录?这需要一些清晰。

答案 1 :(得分:1)

隔离业务逻辑的一种方法是服务对象设计模式。例如:

class TrophyRewarder
  def initialize(winners, last_week_winners)
    @winners = winners
    @last_week_winners = last_week_winners
  end

  def reward(options)
    # All code to determine and store winners comes here
  end
end

您可以将此代码放在app/services文件夹中,然后在控制器中调用它:

trophy_rewarder = TrophyRewarder.new(users, Winners.from_last_week)
trophy_rewarder.reward(options)

好处是您可以从控制器调用此服务对象,也可以从后台任务调用此服务对象。另一个好处是服务对象可以使用许多不同的数据/ AR模型,但不依赖于模型。

我希望这有助于展示如何整理代码。