Rails对类似控制器/视图的最佳实践?

时间:2013-05-28 15:16:16

标签: ruby-on-rails model-view-controller dry

我正在编写库存管理Rails应用程序。我有class Product < ActiveRecord::Baseclass Foo < Productclass Bar < Product。 Foo和Bar的行为略有不同,但使用单表继承对他们来说非常好。

问题在于控制器和视图。目前我将它们完全分开,这有效,但包含大量重复的代码。通过复制两个目录并将@foo@foosFoo替换为@bar@bars和{{1 }}。当我添加新功能时,添加它们两次会很烦人。而且,当然,并不是Rails方式不干。

那么这里的正确方法是什么?对于控制器...我应该制作一个Bar,然后只使用元编程魔法来ProductsControllerFoo?还是使用继承?对于视图,我应该只有产品视图,但是使用聪明的路由使它看起来像我有单独的(和RESTful)Bar/foos路径?

感谢。

3 个答案:

答案 0 :(得分:0)

对于控制器继承将是一个很好,干净和可维护的方法。定义基本控制器中的所有主要逻辑,并使用可在继承控制器中覆盖的虚方法替换对对象的任何调用:

class Product < ApplicationController
  def resource
    nil
  end

  def new_resource
    nil
  end

  def do_something
    resource.do_something
  end

  def new
    @resource = new_resource
  end
end

class Foo < Product
  def resource
    @foo
  end

  def new_resource
    Foo.new
  end
end

继承是非常可维护的,非常干净和清晰,是这类工作的不错选择。

对于视图,您可以为所有常见视图部分创建一个shared目录,这是另一种非常常见的方法,并且允许您以最小的麻烦对URL进行完全RESTful控制。如果你的对象也有类似的方法,那么视图不需要知道它正在处理什么类。

在上面的控制器中,访问foos / new的用户将启动对new_resource的调用,该调用将构建一个新的Foo实例并将其作为@resource返回到页面。

#View:
<%= render 'shared/product_details', :locals => {:product => @resource} %>

答案 1 :(得分:0)

如果您拥有完全相同的控制器方法和视图,则可以将其移至产品 - 这样,您将拥有Product@product@products。由于它是STI,因此FooBar上没有ID重复,因此成员路由也可以。此外,由于模型不同,鸭子打字将负责为每个模型调用正确的方法。

然后,您可以将两个子模型中未重复的所有内容移动到特定控制器。

答案 2 :(得分:0)

根据OP的进一步澄清,我现在有了自己的想法。

模型继承适用于其他OOP语言,但在Ruby中不是必需的。在这种情况下,我认为最好的解决方案是使用Module。

你仍然需要两个型号Foo和Bar,它们甚至可以有不同的属性。

然后,定义模型和控制器的常用方法

# lib/product_model.rb
module ProductModel
  def product_method_a
  end

  def product_method_b
  end
end

# lib/product_controller.rb
module ProductController
  def show
    @obj = @model.constantize.find(params[:id])
    render 'products/show' # need to explicitly specify it because this is general 
  end

  def index
    @objs = @model.constantize.scoped
    render 'products/index' # need to explicitly specify it because this is general 
  end
end

然后,在Foo和Bar中使用这些方法,并根据需要添加自定义方法。

class Foo < ActiveRecord::Base
  include ProductModel

  def serial_number
    # Foo's way
  end
end

class Bar < ActiveRecord::Base
  include ProductModel

  def serial_number
    # Bar's way
  end
end

对于控制器。

class FoosController < ApplicationController
  @model = "foo"
  include ProductController
end


class BarsController < ApplicationController
  @model = "bar"
  include ProductController
end

对于视图,ProductController已经定义了它们,只是为了在视图中使用通用匹配变量名。

<%= @obj.title %>

很干,不是吗?