清理肥胖导轨助手
今天,试图干燥一些代码,我提取了一些重复的 File.exists?将多个辅助方法使用的代码放入私有方法
def template_exists?(*template)
中,并且猴子意外地修补了这个已经存在的 Rails 辅助方法。这是代码味道的一个非常明显的指标,我不需要任何继承的方法,但我继承了它们。此外,我重构的方法在这个助手中到底做了什么?
因此,这个助手做得太多,因此违反了 SRP(单一职责原则)。我觉得 Rails 助手本质上很难保持在 SRP 之内。我正在查看的助手是其他助手的超类助手,本身就有 300 多行。它是一个非常复杂的表单的一部分,使用 javascript 来掌握交互流程。 fat helper 中的方法简短而简洁,所以它并没有那么糟糕,但毫无疑问,它需要关注点分离。
我该怎么走?
- 将方法分成许多助手?
- 将辅助方法内的代码提取到类中并委托给它们?您会确定这些类的范围(即 Mydomain::TemplateFinder)吗?
- 将逻辑分成模块并将它们列为顶部的包含项?
- 其他方法?
正如我所见,对于意外的猴子补丁来说,没有哪两种方法更安全。也许是一个组合?
代码示例和强烈意见表示赞赏!
Today, trying to DRY up some code, I extracted some duplicate File.exists? code used by several helper methods into a private method
def template_exists?(*template)
and accidently monkey patched this already existing Rails helper method. This was a pretty clear indicator of a code smell, I didn't need need any the inherited methods, yet I inherit them. Besides, what was my refactored method doing in this helper at all?
So, this helper does way too much and hence violates SRP (Single Responsibility Principle). I feel that rails helpers are inherently difficult to keep within SRP. The helper I'm looking at is a superclass helper of other helpers, counting 300+ lines itself. It is part of a very complex form using javascript to master the interaction flow. The methods in the fat helper are short and neat, so it's not that awful, but without a doubt, it needs separation of concerns.
How should I go forth?
- Separate the methods into many helpers?
- Extracting the code inside the helpers methods into classes and delegate to them? Would you scope these classes (i.e. Mydomain::TemplateFinder)?
- Separate the logic into modules and list them as includes at the top?
- other approaches?
As I see, no 2 is safer wrt accidential monkeypatching. Maybe a combination?
Code examples and strong opinions appreciated!
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(6)
将帮助程序方法提取到类中(解决方案 n°2)
可以解决这种清理帮助程序的特定方法我首先挖出了这个旧的railscast:
http://railscasts.com/episodes/101-refactoring-out-helper-object
当时它启发了我创建一个小选项卡系统(在我的一个应用程序中与状态机结合使用):
我的观点是这样的:
如您所见,这是一种将逻辑封装在类中并且在帮助器/中只有一个方法的好方法查看空间
Extracting helpers methods into classes (solution n°2)
ok to address this specific way to clean up helpers 1st I dug up this old railscast :
http://railscasts.com/episodes/101-refactoring-out-helper-object
At the time it inspired me to create a little tabbing system (working in one of my apps in conjunction with a state machine) :
And my views go like this :
As you see it's a nice way to encapsulate the logic in a class and have only one method in the helper/view space
我的建议是:不要使用太多自定义结构。如果你有大帮手,好吧,可能就是这种情况。虽然我想知道是否有一个解释为什么整个帮助程序代码不在控制器中。我将帮助程序用于模板内使用的小而简单的方法。复杂的(Ruby-)逻辑应该放入控制器中。如果你真的有一个如此复杂的 Javascript 应用程序,为什么不用 Javascript 编写这个复杂的东西呢?如果确实必须从模板中调用它,那么这就是正确的方法。并且可能会让您的网站更加动态和灵活。
关于猴子修补和命名空间冲突:如果您有听起来很常见的类名、方法名等,请检查它们是否已定义。 Google、grep 或 Rails 控制台。
确保您了解哪些代码属于
帮助器:帮助执行简单的操作,例如创建精美的超链接
def my_awesome_hyperlink 网址,文本
“#{text} 的精美链接”
end
./lib:由多个控制器使用的更复杂的东西和/或也由其他组件(例如 Cucumber 步骤定义)直接使用的东西
My suggestion is: Hesitate from using too many custom structures. If you have large helpers, ok, might be the case. Though I wonder if there is an explanation why that whole helper code is not in the Controller. I use helpers for small and simple methods that are used inside the template. Complex (Ruby-)logic should be put into the Controller. And if you really have such a complex Javascript app, why don't you write this complex stuff in Javascript? If it really has to be called from the template, this is the way to go. And probably makes you website a bit more dynamic and flexible.
Regarding monkey patching and namespace collisions: If you have class name, method names etc. that sound common, check out if they are defined. Google, grep or rails console for them.
Make sure you understand which code belongs to
Helper: Help doing simple stuff like creating a fancy hyperlink
def my_awesome_hyperlink url, text
"Fancy Link to #{text}"
end
./lib: More complex stuff that is used by more than one Controller and/or also used directly by other components like Cucumber step definitions
好吧,这是一个很难回答的问题。 Rails 有点引导你走上视图助手的道路,并且当你不再需要它时,它实际上并没有给你一个像样的内置替代方案。
事实上,助手只是包含在视图对象中的模块,这一事实并不能真正帮助分离关注点和耦合。您需要找到一种方法将该逻辑完全从模块中取出,并在自己的类中找到它的家。
我首先阅读 Presenter 模式,并尝试考虑如何将其应用为视图和模型之间的中间层。尽可能简化视图,并将逻辑移至演示者或模型。将 javascript 移出视图,而是在 .js 文件中编写不显眼的 javascript,以增强现有 javascript 的功能。它绝对不需要在视图中,并且您会发现,如果在视图中填充 js 是让您陷入困境的原因,那么这有助于清理很多内容。
以下是一些阅读链接:
http://blog.jayfields.com/ 2007/03/rails-presenter-pattern.html
关于 Rails 中的演示者模式。是更好的方法吗?
http: //blog.jayfields.com/2007/01/another-rails-presenter-example.html
http://blog.jayfields.com/2007/09/ railsconf-europe-07-presenter-links.html
不要太关注具体的代码示例,而是尝试理解该模式试图实现的目标并思考如何可以将其应用于您的具体问题。 (尽管我真的很喜欢上面第二个链接中的示例;堆栈溢出)。
这有帮助吗?
Ok this is a really hard question to answer. Rails kind of leads you down the path of view helpers and really doesn't give you a decent baked-in alternative when you out-grow it.
The fact that helpers are just modules that are included in to the view object doesn't really help with the separation of concerns and coupling. You need to find a way to take that logic out of modules altogether, and find it a home in its own class.
I'd start by reading up on the Presenter pattern, and try to think of how you might be able to apply it as a middle layer between the view and the model. Simplify the view as much as possible, and move the logic to the presenter or the model. Move javascript right out of the view, and instead write unobtrusive javascript in .js files that enhance the functionality of the existing javascript. It definitely does not need to be in the view, and you'll find that helps clean up a lot if stuffing js in your views is what got you in this mess.
Here's some links for reading:
http://blog.jayfields.com/2007/03/rails-presenter-pattern.html
About presenter pattern in rails. is a better way to do it?
http://blog.jayfields.com/2007/01/another-rails-presenter-example.html
http://blog.jayfields.com/2007/09/railsconf-europe-07-presenter-links.html
Don't get too caught up in the specific code examples, rather try to understand what the pattern is trying to accomplish and think how you could apply it to your specific problem. (though I really like the example in the 2nd link above; the stack overflow one).
Does that help?
如果没有代码,很难提出明确的解决方案。然而,由于辅助方法都位于同一个全局视图实例中,因此名称冲突是一个常见问题。
@Slawosz 可能是一个解决方案,但并不真正适合帮助者哲学。
就我个人而言,我建议使用 cells gem :单元就像 Rails 的组件,除了轻量级、快速、可缓存之外并且可测试。
另外为了回答您的具体问题,它们是完全隔离的。当你的视图助手变得复杂时,它们绝对是解决方案。
(声明我不是这个 gem 的创建者,只是愉快地使用它......)
此外,您还可以在这里使用通用助手来保持干燥,而不必担心名称冲突。
It is difficult to suggest a clear solution without the code. However since helper methods all live in the same global view instance, name collision is a common problem.
@Slawosz may be a solution but no really fit for the helpers philosophy.
Personally I would suggest to use the cells gem : cells are like component for rails, except lightweight, fast, cacheable and testable.
Also to answer your specific problem they're completely isolated. When your view helpers get to complex they're definitely the solution.
(disclosure I am not the creator of this gem, just happily using it....)
Also you can use a generic helper for dryness here without fearing name clash.
我将创建一个小型库,负责操作您的表单。一些命名良好的类,一些继承,并且作为输入您传递 ie 参数,作为输出您可以拥有 ie。此形式中使用的部分对象。一切都将被封装,并且很容易测试。
查看 AbstractController 代码,然后 Metal 到看看导轨的设计有多智能,也许你会找到解决问题的灵感。
I would create a small library which is responsible to manipulate yours forms.Some well named classes, some inheritance, and as input you pass ie parameters, and as output you can has ie. objects for partials used in this form. Everything will be encapsulated, and it will be easy to test.
Look at AbstractController code, and then Metal to see how smart are rails designed, maybe there You will find inspiration how to solve your problem.
您的方法是正确的,但我更喜欢第三点,即将逻辑分成模块。
模块带来了很多可能性,特别是在多个类之间共享代码,因为任意数量的类可以混合在同一个模块中。
模块的最大优势在于它们可以帮助您进行程序设计和灵活性。
使用这种方法您可以实现设计模式。
Your approach is correct but I would prefer 3rd point i.e. separate the logic into modules.
Modules open up lots of possibilities, particularly for sharing code among more than one class, because any number of classes can mix in the same module.
The greatest strength of modules is that they help you with program design and flexibility.
Using such kind of approach you can achieve design patterns.