Rails Group_按反模式
当我使用 Enumerable group_by 方法时,我经常遇到代码异味。我正在重构的一些旧代码就是一个很好的例子
def punctuality_report
params[:date] ? @date = Date.strptime(params[:date], "%m/%d/%Y") : @date = Date.today
params[:through] ? @through = Date.strptime(params[:through], "%m/%d/%Y") : @through = @date + 1
time_range = @date.to_formatted_s(:db)[email protected]_formatted_s(:db)
@orders = Order.daily.includes(:store).where('orders.created_at' => time_range).group_by{|o| o.store}
@orders.each_key.each do |s|
eval "@s#{s.id}_early = @orders[s].collect{|o| o if o.early?}.compact"
eval "@s#{s.id}_avg_early = @s#{s.id}_early.count > 0 ? @s#{s.id}_early.collect{|o| o.earliness}.sum / @s#{s.id}_early.count : '0'"
eval "@s#{s.id}_late = @orders[s].collect{|o| o if o.late?}.compact"
eval "@s#{s.id}_avg_late = @s#{s.id}_late.count > 0 ? @s#{s.id}_late.collect{|o| o.lateness}.sum / @s#{s.id}_late.count : '0'"
eval "@s#{s.id}_on_time = @orders[s] - (@s#{s.id}_early | @s#{s.id}_late)"
end
end
好吧,我正在经历这个,我清楚地看到我们需要将此报告从订单控制器上的操作重构为它自己的模型以清理此实现逻辑。有一个代码味道不好,但我仍然在处理这个orders.group_by 哈希。
问题是当我在视图层时我真的想要那个哈希。我需要从订单中获取摘要,但我需要访问商店。在 Activerecord 中使用组查询方法只会返回一个关系,它不如可枚举的 group_by 哈希有用。我觉得有一个更好的方法来获得我需要的东西并减少大量的查询和 ruby 处理。
I frequently encounter a code smell when I use the Enumerable group_by method. Some old code I'm refactoring is a good example
def punctuality_report
params[:date] ? @date = Date.strptime(params[:date], "%m/%d/%Y") : @date = Date.today
params[:through] ? @through = Date.strptime(params[:through], "%m/%d/%Y") : @through = @date + 1
time_range = @date.to_formatted_s(:db)[email protected]_formatted_s(:db)
@orders = Order.daily.includes(:store).where('orders.created_at' => time_range).group_by{|o| o.store}
@orders.each_key.each do |s|
eval "@s#{s.id}_early = @orders[s].collect{|o| o if o.early?}.compact"
eval "@s#{s.id}_avg_early = @s#{s.id}_early.count > 0 ? @s#{s.id}_early.collect{|o| o.earliness}.sum / @s#{s.id}_early.count : '0'"
eval "@s#{s.id}_late = @orders[s].collect{|o| o if o.late?}.compact"
eval "@s#{s.id}_avg_late = @s#{s.id}_late.count > 0 ? @s#{s.id}_late.collect{|o| o.lateness}.sum / @s#{s.id}_late.count : '0'"
eval "@s#{s.id}_on_time = @orders[s] - (@s#{s.id}_early | @s#{s.id}_late)"
end
end
Okay so I'm coming through this and I see clearly we need to refactor this report out of an action on the orders controller and into a model of its own to clean up this implementation logic. There's one code smell out, but I still struggle with this orders.group_by hash.
The thing is when I am in the view layer I really want that hash. I need to get summaries from the orders but I need access to the stores. Using the group query method in Activerecord just returns me a relation, which is not as useful as the enumerable group_by hash. I feel like there's a better way to get what I need and reduce a lot of this querying and ruby processing.
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(3)
我真的不认为 group_by 方法有什么问题。我确实看到了过度使用 eval 的问题 [-; Eval 的代码味道比 group_by 强烈得多(恕我直言)
话虽这么说,我确实看到了其他一些需要重构的领域:
I don't really see anything wrong with the group_by method. I really see an issue with the overuse of eval though [-; Eval is a much strong code smell (IMHO) than group_by
That being said, I did see some other areas for refactor:
我认为如果使用得当,group_by 是一个完全有效的工具。我在 cron 作业中有一个方法,它使用了一些嵌套的 group_by 语句,我确信这会导致速度显着减慢。我花了2个小时重写了去掉group_by的方法,运行时间从1小时8分钟变成了1小时5分钟。几乎不值得付出努力。
另一方面,该代码存在一些严重的问题。那些三元运算符实际上让我笑出了声,而 eval 语句简直是邪恶的。他们不仅使用了 eval,而且使用得很糟糕。调用 eval 非常慢,并且没有理由不能将所有 5 个 eval 语句合并为一个。
也就是说,即使此方法中只有一条 eval 语句也太多了。我确信有 eval 的时间和地点,但我的代码中从来不需要它。根据我的一般经验,几乎所有对 eval 的调用都可以用 send 代替,这样更快、更安全。
I think group_by is a perfectly valid tool when used appropriately. I had a method in a cron job that used a few nested group_by statements, and I was convinced this was causing significant slowdown. I spent 2 hours rewriting the method to get rid of group_by, and the run-time went from an hour and 8 minutes to an hour and 5 minutes. Hardly worth the effort.
On the other hand, that code has some serious issues. Those ternary operators actually made me laugh out loud, and the eval statements are just evil. Not only did they use eval, they used it badly. Invoking eval is very slow, and there's no reason that all 5 eval statements couldn't have been combined into one.
That said, even one eval statement in this method is too much. I'm sure there's a time and place for eval, but I've never needed it in my code. In my general experience, virtually all calls to eval could be replaced with send, which is much faster and safer.
我的尝试:
简而言之:在
eval
部分中仅添加了实例变量,我们可以使用instance_variable_get
和instance_variable_set
代替。几乎所有eval
的出现都是可以避免的,但我也相信eval
有时会非常有用并且非常强大。代码更清楚地表达了它的意图:它将添加一组实例变量,这些变量一目了然。
我绝对不认为使用
group_by
是一种代码味道。My attempt:
In short: in the
eval
section only instance variables were added, and we can useinstance_variable_get
andinstance_variable_set
instead. Almost all occurences ofeval
can be avoided, but I also believe thateval
sometimes can be extremely useful and very powerful.The code more clearly expresses its intent: it will add a set of instance variables, which is immediately visible on sight.
I definitely do not consider the use of
group_by
to be a code smell.