Ruby on Rails (“Rails”)是一种流行的开源框架,基于 Ruby 编程语言,致力于简化和简化Web应用程序开发过程。
Rails建立在约定优于配置的原则 之上。简单地说,这意味着,默认情况下,Rails假定其 专家开发人员 将遵循“标准”最佳实践约定(对于命名,代码结构等等),如果你这样做,事情将对你有用“auto - 可能“无需指定这些细节。虽然这种范式有其优点,但也存在缺陷。最值得注意的是,框架中幕后发生的“魔术”有时会导致头部混乱,混乱和“发生什么事情?”类型的问题。它在安全性和性能方面也会产生不良后果。
因此,虽然Rails易于使用,但也不难滥用。本教程着眼于10个常见的Rails问题,包括如何避免它们以及它们导致的问题。
Rails基于 MVC架构。在Rails社区,我们一直在讨论 胖模型,瘦模控制器 ,但是我继承的几个最近的Rails应用程序违反了这个原则。将视图逻辑(更好地放在帮助器中)或域/模型逻辑移动到控制器中太容易了。
问题是控制器对象将开始违反 单一责任原则, 使得对代码库的未来更改变得困难且容易出错。通常,控制器中应该具有的唯一逻辑类型是:
虽然这仍然推动了单一责任原则的限制,但它只是Rails框架要求我们在控制器中拥有的最低限度。
开箱即用的Rails模板引擎 ERB是构建具有可变内容的页面的好方法。但是,如果你不小心,你很快就会得到一个大文件,它混合了HTML和Ruby代码,很难管理和维护。这也是一个可能导致大量重复的领域,导致违反 DRY (不要重复自己)的原则。
这可以通过多种方式表现出来。一种是在视图中过度使用条件逻辑。举个简单的例子,考虑一下我们有一个 current_user
返回当前登录用户的方法的情况。通常,在视图文件中最终会出现像这样的条件逻辑结构:
<h3>
Welcome,
<% if current_user %>
<%= current_user.name %>
<% else %>
Guest
<% end %>
</h3>
处理这样的事情的更好方法是确保current_user
始终设置返回的对象 ,无论是否有人登录,并且它以合理的方式回答视图中使用的方法(有时称为空对象) )。例如,您可以 像这样定义 current_user
帮助器 app/controllers/application_controller
:
require 'ostruct'
helper_method :current_user
def current_user
@current_user ||= User.find session[:user_id] if session[:user_id]
if @current_user
@current_user
else
OpenStruct.new(name: 'Guest')
end
end
这将使您能够使用以下一行简单的代码替换以前的视图代码示例:
<h3>Welcome, <%= current_user.name -%></h3>
另外几个推荐的Rails最佳实践:
鉴于最小化视图和控制器中逻辑的指导,MVC架构中唯一放置 所有逻辑的位置都在模型中,对吧?
嗯,不太好。
许多 Rails开发人员 实际上犯了这个错误,并最终坚持他们的ActiveRecord
模型类中的所有内容 导致mongo文件,这不仅违反单一责任原则,而且也是维护的噩梦。
诸如生成电子邮件通知,与外部服务接口,转换为其他数据格式等功能与ActiveRecord
模型的核心责任没有多大关系, 模型应该只是在数据库中查找和保存数据。
因此,如果逻辑不应该进入视图,并且它不应该进入控制器,并且它不应该进入模型,那么它应该去哪里?
输入 普通的旧Ruby对象 (PORO)。使用像Rails这样的综合框架,新的开发人员通常不愿意在框架之外创建自己的类。然而,将模型移出模型进入PORO通常就是医生为避免过于复杂的模型而下令的。使用PORO,您可以将电子邮件通知或API交互等内容封装到自己的类中,而不是将它们粘贴到 ActiveRecord
模型中。
因此,一般来说,在您的模型中应该保留的唯一逻辑是:
ActiveRecord
配置(即关系和验证)full_name
组合 数据库中的字段first_name
和 方法 last_name
)find
); 一般来说,你不应该where
在模型类本身之外使用 方法或任何其他类似的查询构建方法这个错误实际上是上述错误#3的必然结果。如上所述,Rails框架强调MVC框架的命名组件(即模型,视图和控制器)。对于每个组件的类中存在的事物类型有相当好的定义,但有时我们可能需要的方法似乎不适合这三者中的任何一个。
Rails生成器可以方便地构建一个帮助程序目录和一个新的帮助程序类,以与我们创建的每个新资源一起使用。但是,开始将任何不正式适合模型,视图或控制器的功能填充到这些辅助类中变得非常诱人。
虽然Rails肯定是以MVC为中心的,但没有什么能阻止您创建自己的类类型并添加适当的目录来保存这些类的代码。当您有其他功能时,请考虑将哪些方法组合在一起,并为包含这些方法的类找到好名称。使用像Rails这样的综合框架并不是让良好的面向对象设计最佳实践的借口。
Ruby和Rails由丰富的宝石生态系统 提供支持, 这些宝石共同提供开发人员可以想到的任何功能。这对于快速构建复杂的应用程序非常有用,但是我也看到了许多膨胀的应用程序,Gemfile
与所提供的功能相比,应用程序中的宝石数量 不成比例地大。
这会导致几个Rails问题。过度使用宝石会使Rails进程的大小超出其需要。这会降低生产中的性能。除了用户受挫之外,这还可能导致需要更大的服务器内存配置和增加的操作成本。启动更大的Rails应用程序也需要更长的时间,这会使开发变慢并使自动化测试花费更长时间(并且通常,慢速测试不会经常运行)。
请记住,您带入应用程序的每个gem可能反过来依赖于其他gem,而这些gem又可能依赖于其他gem,依此类推。因此,添加其他宝石可以产生复合效果。例如,添加 rails_admin
gem将总共带来11个宝石,比基础Rails安装增加10%。
在撰写本文时,新的Rails 4.1.0安装包含Gemfile.lock
文件中的43个宝石 。这显然不仅包含在内, Gemfile
而且代表了少数标准Rails宝石作为依赖项引入的所有宝石。
在添加每个gem时,请仔细考虑额外的开销是否值得。作为一个例子,开发人员经常随意添加 rails_admin
gem,因为它本质上为模型结构提供了一个漂亮的Web前端,但它实际上只不过是一个花哨的数据库浏览工具。即使您的应用程序要求管理员用户具有其他权限,您可能也不希望为他们提供原始数据库访问权限,并且通过开发自己更简化的管理功能而不是添加此gem,您将获得更好的服务。
虽然大多数Rails开发人员都知道在开发和生产过程中可用的默认日志文件,但他们通常不会对这些文件中的信息给予足够的重视。虽然许多应用程序依赖于Honeybadger 或 New Relic等日志监视工具 ,但在开发和测试应用程序的整个过程中,关注日志文件也很重要。
正如本教程前面提到的,Rails框架为您提供了很多“魔力”,特别是在模型中。在模型中定义关联可以非常轻松地提取关系并让所有内容都可用于您的视图。为您生成填充模型对象所需的所有SQL。那很棒。但是,您怎么知道生成的SQL是有效的?
您经常遇到的一个示例称为 N + 1查询问题。虽然很好地理解了这个问题,但观察它的唯一真正方法是查看日志文件中的SQL查询。
比如说您在典型的博客应用程序中有以下查询,您将在其中显示一组选定帖子的所有评论:
def comments_for_top_three_posts
posts = Post.limit(3)
posts.flat_map do |post|
post.comments.to_a
end
end
当我们查看调用此方法的请求的日志文件时,我们将看到如下所示的内容,其中进行单个查询以获取三个帖子对象,然后再进行三次查询以获取每个对象的注释:
Started GET "/posts/some_comments" for 127.0.0.1 at 2014-05-20 20:05:13 -0700
Processing by PostsController#some_comments as HTML
Post Load (0.4ms) SELECT "posts".* FROM "posts" LIMIT 3
Comment Load (5.6ms) ELECT "comments".* FROM "comments" WHERE "comments"."post_id" = ? [["post_id", 1]]
Comment Load (0.4ms) SELECT "comments".* FROM "comments" WHERE "comments"."post_id" = ? [["post_id", 2]]
Comment Load (1.5ms) SELECT "comments".* FROM "comments" WHERE "comments"."post_id" = ? [["post_id", 3]]
Rendered posts/some_comments.html.erb within layouts/application (12.5ms)
Completed 200 OK in 581ms (Views: 225.8ms | ActiveRecord: 10.0ms)
ActiveRecord
Rails中的 急切加载功能使您可以提前指定要加载的所有关联,从而显着减少查询数量。这是通过在正在构建的Arel()对象上调用includes
(或 preload
)方法来完成的 ActiveRecord::Relation
。使用 includes
, ActiveRecord
确保使用尽可能少的查询加载所有指定的关联; 例如:
def comments_for_top_three_posts
posts = Post.includes(:comments).limit(3)
posts.flat_map do |post|
post.comments.to_a
end
end
执行上述修订代码时,我们在日志文件中看到所有注释都是在一个查询中收集而不是三个:
Started GET "/posts/some_comments" for 127.0.0.1 at 2014-05-20 20:05:18 -0700
Processing by PostsController#some_comments as HTML
Post Load (0.5ms) SELECT "posts".* FROM "posts" LIMIT 3
Comment Load (4.4ms) SELECT "comments".* FROM "comments" WHERE"comments "."post_id" IN (1, 2, 3)
Rendered posts/some_comments.html.erb within layouts/application (12.2ms)
Completed 200 OK in 560ms (Views: 219.3ms | ActiveRecord: 5.0ms)
效率更高。
这个N + 1问题的解决方案实际上只是作为一个例子,说明如果你没有给予足够的重视,你的应用程序中可能存在的“低效率”。这里需要注意的是,您应该在开发期间检查开发和测试日志文件,以检查(并解决!)构建响应的代码中的低效率。
查看日志文件是一种很好的方式,可以在应用程序投入生产之前将代码效率低下并进行纠正。否则,在系统上线之前,您可能不会意识到导致Rails性能问题,因为您在开发和测试中使用的数据集可能比生产中的数据集小得多。如果你正在开发一个新的应用程序,即使你的生产数据集可能很小,你的应用程序看起来运行正常。但是,随着生产数据集的增长,像这样的Rails问题将导致应用程序运行得越来越慢。
如果你发现你的日志文件被堵塞了一堆的信息,你不需要 在这里 有一些事情可以做,以清除它们(这些技术有发展工作以及生产日志)。
来自:https: //www.toptal.com/ruby-on-rails/top-10-mistakes-that-rails-programmers-make