清理胖导轨助手

时间:2011-08-16 14:05:06

标签: ruby-on-rails ruby single-responsibility-principle

今天,我试图干掉一些代码,我提取了一些重复的File.exists?几个辅助方法用于私有方法的代码

def template_exists?(*template)并且意外地猴子修补了这个已经存在的Rails帮助方法。这是代码气味的一个非常明确的指标,我不需要任何继承的方法,但我继承它们。此外,我的重构方法在这个帮助器中做了什么呢?

所以,这个助手做得太多,因此违反了SRP(单一责任原则)。我觉得铁轨助手本身很难保留在SRP中。我正在看的助手是其他助手的超级助手,自己计数300多行。它是使用javascript来掌握交互流的非常复杂形式的一部分。胖助手的方法简短而整洁,所以并不是那么糟糕,但毫无疑问,它需要分离关注点。

我该怎么出去?

  1. 将方法分成多个帮手?
  2. 将helpers方法中的代码提取到类中并委托给它们?你会把这些类(即Mydomain :: TemplateFinder)作为范围吗?
  3. 将逻辑分成模块并将它们列为顶部的包含?
  4. 其他方法?
  5. 正如我所看到的那样,没有2是更安全的经常性的monkeypatching。也许是一个组合?

    代码示例和强烈意见赞赏!

6 个答案:

答案 0 :(得分:4)

将助手方法提取到类中(解决方案n°2)

好的,可以解决这个清理助手的具体方法。首先,我挖出了这个旧的railscast:

http://railscasts.com/episodes/101-refactoring-out-helper-object

当时它激发了我创建一个小标签系统(在我的一个应用程序中与状态机一起工作):

module WorkflowHelper

  # takes the block  
  def workflow_for(model, opts={}, &block)
    yield Workflow.new(model, opts[:match], self)
    return false
  end

  class Workflow
    def initialize(model, current_url, view)
      @view = view
      @current_url = current_url
      @model = model
      @links = []
    end

    def link_to(text, url, opts = {})
      @links << url
      url = @model.new_record? ? "" : @view.url_for(url)
      @view.raw "<li class='#{active_class(url)}'>#{@view.link_to(text, url)}</li>"
    end

  private
    def active_class(url)
      'active' if @current_url.gsub(/(edit|new)/, "") == url.gsub(/(edit|new)/, "") ||
                 ( @model.new_record? && @links.size == 1 )
    end

  end #class Workflow
end

我的观点是这样的:

  -workflow_for @order, :match => request.path do |w|
    = w.link_to "✎ Create/Edit an Order", [:edit, :admin, @order]
    = w.link_to "√ Decide for Approval/Price", [:approve, :admin, @order]
    = w.link_to "✉ Notify User of Approval/Price", [:email, :admin, @order]
    = w.link_to "€ Create/Edit Order's Invoice", [:edit, :admin, @order, :invoice] 

如您所见,这是一种将逻辑封装在类中并且在辅助/视图空间中只有一个方法的好方法

答案 1 :(得分:2)

好的,这是一个非常难以回答的问题。 Rails有点引导你走向视线助手的道路,当你超越它时,它真的不会给你一个体面的烘焙替代品。

帮助程序只是包含在视图对象中的模块这一事实并不能真正帮助分离关注点和耦合。你需要找到一种方法将这些逻辑从模块中完全取出,并在它自己的类中找到它。

我首先阅读Presenter模式,然后试着想一想如何将它作为视图和模型之间的中间层应用。尽可能简化视图,并将逻辑移动到演示者或模型。将javascript移出视图,然后在.js文件中编写不显眼的javascript,增强现有javascript的功能。它绝对不需要在视图中,你会发现如果在你的视图中填充js是让你陷入困境的话,那么有助于清理很多。

以下是阅读的一些链接:

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

不要过于关注特定的代码示例,而是尝试了解该模式试图完成的内容,并思考如何将其应用于您的特定问题。 (虽然我非常喜欢上面第二个链接中的示例;堆栈溢出一个)。

这有帮助吗?

答案 2 :(得分:1)

如果没有代码,很难建议一个明确的解决方案。但是,由于辅助方法都存在于同一个全局视图实例中,因此名称冲突是一个常见问题。

@Slawosz可能是一个解决方案,但并不适合帮助者的理念。

我个人建议使用cells gem:cell类似于rails的组件,除了轻量级,快速,可缓存和可测试之外。

另外,为了解答您的具体问题,他们完全是孤立的。当您的视图助手变得复杂时,他们肯定是解决方案。

(披露我不是这个宝石的创造者,只是高兴地使用它......)

# some view
= render_cell :cart, :display, :user => @current_user

# cells/cart_cell.rb
# DO whatever you like in here
class CartCell < Cell::Rails

  include SomeGenericHelper

  def display(args)
    user    = args[:user]
    @items  = user.items_in_cart

    render  # renders display.html.haml
  end
end

此外,你可以在这里使用通用助手干燥,而不必担心名字冲突。

答案 3 :(得分:1)

  1. 你的意思是将大帮助者分成小帮手?为什么不。我不太了解您的代码,但您可能需要考虑将大型代码块外包到./lib。
  2. 否。 ;-)这听起来非常复杂。
  3. 听起来也很复杂。与1:./lib相同的建议。如果您访问它们,那里的模块会自动加载。
  4. 没有
  5. 我的建议是:使用太多自定义结构时犹豫不决。如果你有大帮手,那么可能就是这样。虽然我想知道是否有一个解释为什么整个辅助代码不在Controller中。我使用帮助程序来处理模板中使用的小而简单的方法。应将复杂(Ruby-)逻辑放入Controller中。如果你真的有这么复杂的Javascript应用程序,为什么不在Javascript中编写这些复杂的东西呢?如果真的必须从模板调用,这就是要走的路。并且可能会让您的网站更加动态和灵活。

    关于猴子修补和命名空间冲突:如果您有类名,方法名等,这听起来很常见,请查看它们是否已定义。谷歌,grep或rails控制台。

    确保您了解哪些代码属于

    • 控制器:用填充填充变量,执行用户操作(基本上是页面背后的计算)
    • 助手:帮助做一些简单的事情,比如创建一个花哨的超链接

      def my_awesome_hyperlink url,text   “#{text}的精彩链接” 端

    • ./ lib:多个控制器使用的更复杂的东西和/或黄瓜步骤定义等其他组件直接使用的东西

    • 在模板内部作为Ruby代码:超级易于阅读
    • 在模板(或./public)内部作为Javascript代码:品味的问题。但是你的应用程序越动态,代码就越有可能在这里。

答案 4 :(得分:0)

我会创建一个小型库,负责处理你的表单。一些名称很好的类,一些继承,作为输入你传递即参数,作为输出,你可以有ie。此表单中使用的部分对象。一切都将被封装,并且很容易测试。

查看AbstractController代码,然后看看Metal,看看有多聪明的rails设计,也许那里你会找到解决问题的灵感。

答案 5 :(得分:-1)

你的方法是正确的,但我更喜欢第三点,即将逻辑分成模块。

模块开辟了许多可能性,特别是在多个类之间共享代码,因为任何数量的类都可以混合在同一个模块中。

模块的最大优势在于它们可以帮助您进行程序设计和灵活性。

使用这种方法可以实现设计模式。