4

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

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

所以,这个助手做得太多,因此违反了 SRP(单一责任原则)。我觉得 Rails 助手本质上很难保留在 SRP 中。我正在查看的助手是其他助手的超类助手,它本身就有 300 多行。它是一个非常复杂的表单的一部分,使用 javascript 来掌握交互流程。fat helper 中的方法短小精悍,所以没有那么糟糕,但毫无疑问,它需要关注点分离。

我该怎么走?

  1. 将方法分成许多助手?
  2. 将辅助方法中的代码提取到类中并委托给它们?你会限定这些类(即 Mydomain::TemplateFinder)吗?
  3. 将逻辑分成模块并将它们列为顶部的包含?
  4. 其他方法?

正如我所看到的,没有 2 更安全 wrt 意外的猴子补丁。也许是一个组合?

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

4

6 回答 6

4

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

可以解决这种清理助手的特定方法 1st 我挖出了这个旧的 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] 

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

于 2011-08-25T17:10:56.667 回答
2
  1. 你的意思是把大帮手分成小帮手?为什么不。我不太了解您的代码,但您可能需要考虑将大代码块外包到 ./lib。
  2. 不。;-) 这听起来非常复杂。
  3. 听起来也很复杂。与 1 中的建议相同:./lib. 如果您访问其中的模块,则会自动加载它们。

我的建议是:不要使用太多的自定义结构。如果你有大帮手,好吧,可能就是这种情况。虽然我想知道是否有解释为什么整个帮助代码不在控制器中。我将帮助器用于模板内使用的小而简单的方法。复杂的(Ruby-)逻辑应该放入控制器中。如果你真的有这么复杂的 Javascript 应用程序,为什么不用 Javascript 编写这些复杂的东西呢?如果真的必须从模板中调用它,这就是要走的路。并且可能使您的网站更加动态和灵活。

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

确保您了解哪些代码属于

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

    def my_awesome_hyperlink url, text "Fancy Link to #{text}" end

  • ./lib:由多个控制器使用和/或也由其他组件(如 Cucumber 步骤定义)直接使用的更复杂的东西

  • 在模板中作为 Ruby 代码:超级易读
  • 在模板(或 ./public)内作为 Javascript 代码:口味问题。但是你的应用程序越动态,代码就越有可能属于这里。
于 2011-08-23T16:43:07.060 回答
2

好的,这是一个很难回答的问题。Rails 有点引导你走上视图助手的道路,当你超越它时,它真的没有给你一个像样的烘焙替代品。

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

我将从阅读 Presenter 模式开始,并尝试考虑如何将它应用为视图和模型之间的中间层。尽量简化视图,把逻辑移到presenter或者model。将 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

不要太拘泥于特定的代码示例,而是尝试理解该模式试图完成的任务,并思考如何将其应用于您的特定问题。(尽管我真的很喜欢上面第二个链接中的示例;堆栈溢出之一)。

这有帮助吗?

于 2011-08-26T06:09:39.957 回答
1

没有代码很难提出明确的解决方案。然而,由于辅助方法都存在于同一个全局视图实例中,名称冲突是一个常见问题。

@Slawosz 可能是一个解决方案,但并不真正适合助手哲学。

就我个人而言,我建议使用单元格 gem:单元格就像轨道的组件,除了轻量级、快速、可缓存和可测试。

还要回答您的具体问题,它们是完全隔离的。当您的视图助手变得复杂时,它们绝对是解决方案。

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

# 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

您也可以在这里使用通用助手来干燥,而不必担心名称冲突。

于 2011-08-22T12:25:49.600 回答
0

我会创建一个小型库,负责操作您的表单。一些命名良好的类,一些继承,以及作为输入传递 ie 参数,作为输出,您可以使用 ie。在这种形式中使用的部分对象。一切都会被封装起来,并且很容易测试。

看看 AbstractController的代码,然后看 Metal 看看 rails 的设计有多聪明,说不定你会找到解决问题的灵感。

于 2011-08-22T11:15:22.613 回答
-1

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

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

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

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

于 2011-08-16T15:29:51.537 回答