我真的很喜欢Rails(即使我通常是RESTless的),我也很喜欢Ruby。尽管如此,创建大型ActiveRecord子类和大型控制器的趋势还是很自然的(即使您确实为每个资源使用一个控制器)。如果要创建更深的对象世界,您将把类(和我想的模块)放在哪里?我问的是视图(在Helpers本身中),控制器和模型。
Lib没关系,我已经找到了一些解决方案可以在开发环境中重新加载它,但我想知道是否有更好的方法可以做到这一点。我真的只是担心班级过大。此外,引擎又如何适应引擎呢?
我真的很喜欢Rails(即使我通常是RESTless的),我也很喜欢Ruby。尽管如此,创建大型ActiveRecord子类和大型控制器的趋势还是很自然的(即使您确实为每个资源使用一个控制器)。如果要创建更深的对象世界,您将把类(和我想的模块)放在哪里?我问的是视图(在Helpers本身中),控制器和模型。
Lib没关系,我已经找到了一些解决方案可以在开发环境中重新加载它,但我想知道是否有更好的方法可以做到这一点。我真的只是担心班级过大。此外,引擎又如何适应引擎呢?
Answers:
由于Rails提供了基于MVC的结构,因此自然而然地只使用为您提供的模型,视图和控制器容器。对于初学者(甚至是一些中级程序员)而言,典型的习惯用法是将应用程序中的所有逻辑塞入模型(数据库类),控制器或视图中。
在某些时候,有人指出“胖模型,瘦控制器”范例,中间开发人员匆忙从控制器中删除所有内容,并将其扔进模型中,这开始成为应用逻辑的新垃圾桶。
瘦控制器实际上是一个好主意,但是将模型中的所有内容推论得出的推论并不是真正的最佳计划。
在Ruby中,您有几个不错的选择,可以使事情更加模块化。一个比较流行的答案是只使用lib
包含方法组的模块(通常藏在中),然后将模块包含在适当的类中。如果您有希望在多个类中重用的功能类别,但仍在功能上仍附加在类上的情况下,这将很有帮助。
请记住,当您将模块包含到类中时,这些方法将成为该类的实例方法,因此您最终仍会得到一个包含大量方法的类,它们被很好地组织为多个文件。
该解决方案在某些情况下可以很好地工作-在其他情况下,您将要考虑在代码中使用不是模型,视图或控制器的类。
考虑这一点的一个好方法是“单一责任原则”,它说一个类应该对一件(或少量)事物负责。您的模型负责将数据从应用程序持久存储到数据库中。您的控制器负责接收请求并返回可行的响应。
如果您有不完全满足需要的那些箱子(持久性,请求/响应管理)的概念,你可能要考虑如何将有问题的想法建模。您可以在应用程序/类中或其他任何位置存储非模型类,然后通过执行以下操作将该目录添加到加载路径中:
config.load_paths << File.join(Rails.root, "app", "classes")
如果您使用的是Passenger或JRuby,则可能还希望将路径添加到热切的加载路径中:
config.eager_load_paths << File.join(Rails.root, "app", "classes")
最重要的是,一旦到达Rails的某个位置,您就会发现自己在问这个问题,现在该增强您的Ruby Chop并开始建模不仅仅是Rails默认为您提供的MVC类的类。
更新:此答案适用于Rails 2.x及更高版本。
更新:在Rails 4中,已确认使用关注点作为新的默认设置。
这实际上取决于模块本身的性质。我通常将控制器/模型扩展名放在应用程序的/ concerns文件夹中。
# concerns/authentication.rb
module Authentication
...
end
# controllers/application_controller.rb
class ApplicationController
include Authentication
end
# concerns/configurable.rb
module Configurable
...
end
class Model
include Indexable
end
# controllers/foo_controller.rb
class FooController < ApplicationController
include Indexable
end
# controllers/bar_controller.rb
class BarController < ApplicationController
include Indexable
end
/ lib是通用库的首选。我在lib中始终有一个项目名称空间,我在其中放置了所有特定于应用程序的库。
/lib/myapp.rb
module MyApp
VERSION = ...
end
/lib/myapp/CacheKey.rb
/lib/myapp/somecustomlib.rb
Ruby / Rails核心扩展通常在配置初始化程序中进行,因此库仅在Rails boostrap上加载一次。
/config/initializer/config.rb
/config/initializer/core_ext/string.rb
/config/initializer/core_ext/array.rb
对于可重用的代码片段,我经常创建(微)插件,以便可以在其他项目中重用它们。
帮助对象(例如,表单生成器)打算使用该对象时,帮助文件通常包含帮助方法,有时还包含类。
这是一个非常一般的概述。如果您想获得更多定制建议,请提供有关特定示例的更多详细信息。:)
...产生大型ActiveRecord子类和大型控制器的趋势很自然...
“巨大”是一个令人担忧的词... ;-)
您的控制器如何变得庞大?您应该看一下:理想情况下,控制器应该薄。我凭空选择一个经验法则,建议您如果每个控制器方法(动作)经常有5行或6行以上的代码,则您的控制器可能太胖了。是否存在可能移入辅助功能或过滤器的重复项?是否存在可以推入模型的业务逻辑?
您的模型如何变得庞大?您是否应该寻找减少每个班级职责数量的方法?您可以将任何常见行为提取到mixins中吗?还是可以委托给助手类的功能领域?
编辑:尝试扩展一点,希望不会扭曲得太严重...
助手:居住在这里app/helpers
,通常用于简化视图。它们要么是特定于控制器的(也可用于该控制器的所有视图),要么通常是可用的(module ApplicationHelper
在application_helper.rb中)。
过滤器:假设您在多个操作中有相同的代码行(经常,使用params[:id]
或类似方法检索对象)。可以先在类定义中声明一个过滤器,例如,然后将抽象复制到一个单独的方法中,然后再从所有操作中抽象出来before_filter :get_object
。请参见《ActionController Rails指南》中的第6节。让声明式编程成为您的朋友。
重构模型更多是出于宗教目的。例如,鲍伯叔叔的门徒会建议您遵循SOLID的五诫。乔尔和杰夫(Joel&Jeff)可能会建议一种更为错误的“务实”方法,尽管此后似乎确实有所调和。在类中的一个明确地定义其属性的子集上查找一个或多个方法是尝试识别可能从ActiveRecord派生的模型中重构的类的一种方法。
顺便说一下,Rails模型不必是ActiveRecord :: Base的子类。换句话说,模型不必是表的类似物,甚至不必与任何存储的东西相关。甚至更好的是,只要您app/models
按照Rails的约定命名文件(在类名上调用#underscore以查找Rails会查找的内容),Rails便会找到它,而无需进行任何操作require
。
这是一篇关于重构胖模型的优秀博客文章,该胖模型似乎源于“瘦控制器”理念:
http://blog.codeclimate.com/blog/2012/10/17/7-ways-to-decompose-fat-activerecord-models/
基本消息是“不要从Fat模型中提取Mixins”,而是使用服务类,作者提供了7种模式