正确的 Rails 大量逻辑方法
Correct Rails Method for Lots of Logic
我目前正在建设一个每周 运行 进行自主竞赛的网站。我的逻辑检查前一周是否分配了获胜者,如果没有,它会滚动,找到获胜者并分配奖杯。
逻辑一切正常,但其中很多是在应用程序控制器中 运行,在我的内心深处,我觉得这是不对的。
比如第一名比第二名多,第二名比第三名多,就需要创建一个第一名第二名和第三名的奖杯,奖励给正确的用户。
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
这是将奖杯直接构建到控制器中,我经常听到 'fat model skinny controller' 的争论,我觉得我 运行 认为这完全相反!
如何将奖杯作品移动到模型中?我确信我可以在周模型中使用类似 after_save
的东西,但我不完全确定如何保持逻辑正常工作。我尝试了几次,但经常收到错误 undefined method to_model
.
我知道我可以继续努力让它发挥作用,但我只是觉得这不是做事的 'Rails Way',所以我想在早期阶段解决它。
非常感谢任何帮助。
谢谢!
根据评论编辑:
感谢您抽出宝贵时间查看此内容。简而言之 shell,我想要实现的是一个从周一到周日 运行 比赛的系统。 'Active Week' 的范围是 Date.today 介于 :start_date 和 :end_date.
之间的那一周
下周一开始新的一周,这会将活跃的一周移到前一周,然后将奖杯分配给前一周的前 3 名条目。它通过检查前一周是否有 winner_id 来分配奖杯。如果是,则没有 运行 任何逻辑,但是一旦范围移动到新的一周,前几周 :winner_id 现在为零,因此逻辑 运行s新一周有人第一次来本站。
简化一下:
- 周是资源,其中has_many条目。
- 一个条目 belongs_to 个用户,belongs_to 个星期,has_many 个投票。
- 一个投票belongs_to一个条目
- 一个用户 has_many 个条目和 has_many 个奖杯
- 一个奖杯属于一个用户,belongs_to一个星期
因此,用户在当前活跃周对条目进行投票。一旦该周超出活动范围,它就会为本周排名前 3 位的用户创建奖杯。
在不了解上下文的情况下很难给出好的建议。但引起我注意的是,该代码中有很多重复,并且您在这些重复中更新了用户奖杯。因此,作为第一步,我至少会将该逻辑转移到用户中:
# 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
class。取决于上下文...
而且我注意到你的 week_entry
有一个 user
。然后您向 user
添加一个 trophy
,它又具有一些 user
字段。这不会导致循环依赖吗?或者您是否使用完全相同的条目覆盖现有记录?这需要一些清晰度。
隔离业务逻辑的一种方法是服务对象设计模式。示例:
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)
好处是您可以从控制器调用此服务对象,也可以从后台任务调用。另一个好处是服务对象可以使用很多不同的 data/AR 模型,但它并不依赖于模型。
我希望这对展示如何组织代码有所帮助。
我目前正在建设一个每周 运行 进行自主竞赛的网站。我的逻辑检查前一周是否分配了获胜者,如果没有,它会滚动,找到获胜者并分配奖杯。
逻辑一切正常,但其中很多是在应用程序控制器中 运行,在我的内心深处,我觉得这是不对的。
比如第一名比第二名多,第二名比第三名多,就需要创建一个第一名第二名和第三名的奖杯,奖励给正确的用户。
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
这是将奖杯直接构建到控制器中,我经常听到 'fat model skinny controller' 的争论,我觉得我 运行 认为这完全相反!
如何将奖杯作品移动到模型中?我确信我可以在周模型中使用类似 after_save
的东西,但我不完全确定如何保持逻辑正常工作。我尝试了几次,但经常收到错误 undefined method to_model
.
我知道我可以继续努力让它发挥作用,但我只是觉得这不是做事的 'Rails Way',所以我想在早期阶段解决它。
非常感谢任何帮助。
谢谢!
根据评论编辑:
感谢您抽出宝贵时间查看此内容。简而言之 shell,我想要实现的是一个从周一到周日 运行 比赛的系统。 'Active Week' 的范围是 Date.today 介于 :start_date 和 :end_date.
之间的那一周下周一开始新的一周,这会将活跃的一周移到前一周,然后将奖杯分配给前一周的前 3 名条目。它通过检查前一周是否有 winner_id 来分配奖杯。如果是,则没有 运行 任何逻辑,但是一旦范围移动到新的一周,前几周 :winner_id 现在为零,因此逻辑 运行s新一周有人第一次来本站。
简化一下:
- 周是资源,其中has_many条目。
- 一个条目 belongs_to 个用户,belongs_to 个星期,has_many 个投票。
- 一个投票belongs_to一个条目
- 一个用户 has_many 个条目和 has_many 个奖杯
- 一个奖杯属于一个用户,belongs_to一个星期
因此,用户在当前活跃周对条目进行投票。一旦该周超出活动范围,它就会为本周排名前 3 位的用户创建奖杯。
在不了解上下文的情况下很难给出好的建议。但引起我注意的是,该代码中有很多重复,并且您在这些重复中更新了用户奖杯。因此,作为第一步,我至少会将该逻辑转移到用户中:
# 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
class。取决于上下文...
而且我注意到你的 week_entry
有一个 user
。然后您向 user
添加一个 trophy
,它又具有一些 user
字段。这不会导致循环依赖吗?或者您是否使用完全相同的条目覆盖现有记录?这需要一些清晰度。
隔离业务逻辑的一种方法是服务对象设计模式。示例:
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)
好处是您可以从控制器调用此服务对象,也可以从后台任务调用。另一个好处是服务对象可以使用很多不同的 data/AR 模型,但它并不依赖于模型。
我希望这对展示如何组织代码有所帮助。