我真的很喜欢Rails(尽管我通常不安分),我也喜欢Ruby非常面向对象。尽管如此,创建大型ActiveRecord子类和大型控制器的趋势是很自然的(即使您确实为每个资源使用一个控制器)。如果您要创建更深层次的对象世界,您将把类(以及模块)放在哪里?我问的是视图(在助手本身?),控制器和模型。

Lib是可以的,我已经找到了一些解决方案,让它在开发环境中重新加载,但我想知道是否有更好的方法来做这件事。我只是担心班级太大了。此外,关于引擎,它们是如何融入其中的?


当前回答

因为Rails以MVC的方式提供结构,所以最终只使用提供给您的模型、视图和控制器容器是很自然的。对于初学者(甚至一些中级程序员)来说,典型的习惯是将应用程序中的所有逻辑塞进模型(数据库类)、控制器或视图中。

在某个时候,有人指出了“胖模型、瘦控制器”的范例,中间开发人员匆忙地从控制器中删除所有内容并将其扔进模型,这开始成为应用程序逻辑的一个新的垃圾桶。

事实上,瘦控制器是个好主意,但其推论——把所有东西都放在模型中,并不是最好的计划。

在Ruby中,有几个很好的选项可以使事情更加模块化。一个相当流行的答案是只使用包含一组方法的模块(通常存储在lib中),然后将这些模块包含到适当的类中。这有助于您拥有希望在多个类中重用的功能类别,但功能仍然在概念上附加到类。

记住,当你将一个模块包含到一个类中时,方法就变成了类的实例方法,所以你最终得到的仍然是一个包含大量方法的类,它们只是被很好地组织到多个文件中。

这种解决方案在某些情况下可以很好地工作——在其他情况下,您将考虑在代码中使用不是模型、视图或控制器的类。

考虑这个问题的一个好方法是“单一责任原则”,它说一个类应该负责单一(或少量)的事情。您的模型负责将数据从应用程序持久化到数据库。控制器负责接收请求并返回可行的响应。

如果您有一些概念不能完全符合这些框框(持久性、请求/响应管理),那么您可能需要考虑如何对所讨论的想法建模。你可以将非模型类存储在app/classes中,或其他任何地方,并将该目录添加到你的加载路径:

config.load_paths << File.join(Rails.root, "app", "classes")

如果你正在使用passenger或JRuby,你可能还想将你的路径添加到急切加载路径:

config.eager_load_paths << File.join(Rails.root, "app", "classes")

最重要的是,一旦您在Rails中发现自己问了这个问题,就应该加强Ruby的能力并开始建模类,而不仅仅是Rails默认提供给您的MVC类。

更新:这个答案适用于Rails 2。X和更高。

其他回答

更新:关注点的使用已被确认为Rails 4中的新默认设置。

这实际上取决于模块本身的性质。 我通常把控制器/模型扩展放在应用程序的/关注点文件夹中。

# 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启动时加载一次。

/config/initializer/config.rb
/config/initializer/core_ext/string.rb
/config/initializer/core_ext/array.rb

对于可重用的代码片段,我经常创建(微)插件,这样我就可以在其他项目中重用它们。

Helper文件通常包含Helper方法,当对象打算被Helper使用时(例如Form Builders),有时还包含类。

这是一个非常笼统的概述。如果你想获得更多定制化的建议,请提供更多具体例子的细节。:)

... 倾向于做巨大的 ActiveRecord子类和巨大的 控制器是很自然的…

“巨大”是一个令人担忧的词……: -)

你的控制器是如何变得巨大的?这是你应该注意的:理想情况下,控制器应该很薄。根据经验,我建议如果每个控制器方法(动作)的代码经常超过5或6行,那么你的控制器可能太胖了。是否存在可以移动到辅助函数或过滤器的复制?是否存在可以下推到模型中的业务逻辑?

你的模型是怎么变大的?你是否应该设法减少每个类的责任数量?是否有一些常见的行为可以提取到mixin中?或者可以委托给助手类的功能领域?

编辑:试着扩大一点,希望没有扭曲任何东西太严重…

helper:存在于app/ Helpers中,主要用于简化视图。它们要么是特定于控制器的(也适用于该控制器的所有视图),要么是一般可用的(application_helper.rb中的ApplicationHelper模块)。

过滤器:假设在几个操作中有同一行代码(通常是使用参数[:id]或类似的方法检索对象)。通过在类定义中声明一个过滤器(例如before_filter:get_object),可以首先将复制抽象为一个单独的方法,然后完全从操作中分离出来。让声明式编程成为你的朋友。

重构模型更像是一件宗教的事情。例如,鲍勃叔叔的门徒会建议你遵守SOLID的五诫。乔尔和杰夫可能会推荐一种更“务实”的方法,尽管他们后来确实表现得更和解了。在类中找到一个或多个操作其属性的明确定义子集的方法,是尝试识别可能从activerecord派生模型中重构出来的类的一种方法。

顺便说一下,Rails模型不一定是ActiveRecord::Base的子类。或者换句话说,模型不必是表的模拟,甚至不必与存储的任何东西相关。更好的是,只要你在app/models中根据Rails的约定命名你的文件(在类名上调用#下划线来查找Rails要查找的内容),Rails就会找到它,而不需要任何必要的要求。

这里有一篇关于重构胖模型的优秀博客文章,它似乎来自“瘦控制器”哲学:

http://blog.codeclimate.com/blog/2012/10/17/7-ways-to-decompose-fat-activerecord-models/

基本的信息是“不要从胖模型中提取Mixins”,使用服务类代替,作者提供了7个模式来这样做

因为Rails以MVC的方式提供结构,所以最终只使用提供给您的模型、视图和控制器容器是很自然的。对于初学者(甚至一些中级程序员)来说,典型的习惯是将应用程序中的所有逻辑塞进模型(数据库类)、控制器或视图中。

在某个时候,有人指出了“胖模型、瘦控制器”的范例,中间开发人员匆忙地从控制器中删除所有内容并将其扔进模型,这开始成为应用程序逻辑的一个新的垃圾桶。

事实上,瘦控制器是个好主意,但其推论——把所有东西都放在模型中,并不是最好的计划。

在Ruby中,有几个很好的选项可以使事情更加模块化。一个相当流行的答案是只使用包含一组方法的模块(通常存储在lib中),然后将这些模块包含到适当的类中。这有助于您拥有希望在多个类中重用的功能类别,但功能仍然在概念上附加到类。

记住,当你将一个模块包含到一个类中时,方法就变成了类的实例方法,所以你最终得到的仍然是一个包含大量方法的类,它们只是被很好地组织到多个文件中。

这种解决方案在某些情况下可以很好地工作——在其他情况下,您将考虑在代码中使用不是模型、视图或控制器的类。

考虑这个问题的一个好方法是“单一责任原则”,它说一个类应该负责单一(或少量)的事情。您的模型负责将数据从应用程序持久化到数据库。控制器负责接收请求并返回可行的响应。

如果您有一些概念不能完全符合这些框框(持久性、请求/响应管理),那么您可能需要考虑如何对所讨论的想法建模。你可以将非模型类存储在app/classes中,或其他任何地方,并将该目录添加到你的加载路径:

config.load_paths << File.join(Rails.root, "app", "classes")

如果你正在使用passenger或JRuby,你可能还想将你的路径添加到急切加载路径:

config.eager_load_paths << File.join(Rails.root, "app", "classes")

最重要的是,一旦您在Rails中发现自己问了这个问题,就应该加强Ruby的能力并开始建模类,而不仅仅是Rails默认提供给您的MVC类。

更新:这个答案适用于Rails 2。X和更高。