我们所有人都使用过著名的Devisegem来进行Rails认证,但我们也可以说它是一个引擎。 所以,简单地说,Rails引擎是Rails应用程序的缩影。 它有自己的应用程序文件夹,其中包括控制器、模型等。唯一不同的是,引擎不能独立工作,因此,为了使其工作,我们需要将其注入主Rails应用程序中。
Rails引擎的出现是为了通过创建模块化和可重用的组件来处理臃肿的架构,但是,如果过度使用,一切都会成为一种诅咒。
如果使用经过深思熟虑的设计来实现,Rails引擎可以成为一种强大的资产,否则它可能导致各种问题。
向引擎泄露应用程序的信息
一个应用程序可能会向引擎泄露信息,而引擎可能会使用这些信息来做一些意想不到的事情。
比方说,一个引擎期望从主应用程序中得到一个author_name ,它试图用这个Post ,并将用户与 ,由于数据库是共享的,如果没有找到,引擎会在主应用程序不知情的情况下创建一个User ,这可能导致严重的问题。
module Engine
class Post < ActiveRecord::Base
attr_accessor :author_name
belongs_to :author, class_name: "User"
before_validation :set_author
private
def set_author
self.author = User.find_or_create_by(name: author_name)
end
end
end
在这里,我们也可以注意到,引擎对数据库有完全的控制权,可以对用户进行任何的操作。
另外,例如,假设我们的主应用程序有一个posts_controller 和一个路由帮助器posts_path ,如果我们的引擎试图使用一个帮助器posts_path ,那么它可以使用它而不出错,所以,这就是事情如何通过主应用程序和引擎之间的边界泄漏的。
不要使用除非是跨代码库共享的代码
除非我们计划在项目之间共享引擎,否则使用引擎是没有意义的。 引擎没有给架构增加什么有意义的封装,它只是一种将一个Rails项目混入另一个项目的方式。 如果我们计划将下一个Devise作为一个gem发布,这就很有用。 但同时,将gem作为我们项目的关键部分也是一种痛苦,因为根据定义,它比项目的其他部分更难改变,所以,我们总是可以先用Ruby模块或libs来尝试。
更难测试引擎代码
引擎使TDD几乎不可能。 引擎需要有自己完整的假Rails应用和数据库来运行任何测试,这对任何项目的开发团队来说都是一笔开销。
要编写依赖于主应用程序模型的引擎测试,我们需要在引擎中定义的test/dummy 目录中生成主应用程序模型,并且需要在这个假的应用程序上安装引擎,正如文档中提到的。
较少的内聚性和与应用程序代码的耦合性
凝聚力的概念是让应用程序可以独立发展,这意味着如果我们改变一个组件,那么我们就不需要改变另一个组件,但在引擎的情况下,这不是真的,例如,我们的博客引擎通过name 找到一个用户,如果我们把一个name 属性重命名为username ,我们将需要更新引擎代码以使用这个新属性。
module Engine
class Post < ActiveRecord::Base
attr_accessor :author_name
belongs_to :author, class_name: "User"
before_validation :set_author
private
def set_author
self.author = User.find_or_create_by(name: author_name)
end
end
end
这些问题的发生是因为我们一般没有在我们的应用和引擎之间定义一个有边界的上下文。
另外,引擎和应用程序使用的是同一个数据库。 通过创建一个与我们的数据存储分离的接口,我们顺利地实现了拥有自己的数据存储的完全解耦服务的道路。 此外,我们明确地定义了与数据的交互和引擎内的行为,而不是将数据库作为一个接口。
不必要的代码和依赖性
有一些不必要的和多余的代码被检查到VCS中,因为引擎有自己的应用目录和.gemspec 文件。 因此,在主应用程序中,我们也需要照顾到引擎,以清理不必要的代码和依赖关系。
多个配置和gemfiles。
在使用引擎时,我们需要处理为主应用程序和引擎创建单独的配置,这可能导致一些微妙的严重问题。
引擎用.gemspec ,而不是Gemfile ,这意味着我们的宝石需要在RubyGems这样的宝石服务器上提供,而不能直接从GitHub加载。 我们总是可以在本地使用宝石,但这很麻烦。 我们还需要注意在引擎和主应用之间使用相似的依赖版本。 例如,如果我们的主应用程序使用的是Devise 4.0,而引擎使用的是Devise 3.0的不同配置,那么引擎将不能像预期的那样工作,因为引擎devise会从主应用程序的初始化器中加载配置,而不是从引擎的初始化器中加载。