• 开发新手最容易犯的50个 Ruby on Rails 错误(1)


    【编者按】本文最早发布与 JETRuby 博客,主要介绍了开发新手最容易犯的 Ruby 错误。文章系国内 ITOM 管理平台 OneAPM 编译呈现。

    一年前,我们创立了以 “Rubyboost” 为名的 Ruby on Rails 课程。简而言之,本课程的目标是使对编程了解不多的新手也能在两个月内,提升技能、成为初级开发者。在成功完成课程之后,学生会收到为其两个月的实习邀请,实习地点就在我们公司。如果一切顺利,就会得到聘用。不得不说,这是一种相对公平且简单的成为职业开发者的道路,你觉得呢?

    顺带说一句,你根本想不到,有多少人愿意来参加并学习 Rails 编程!

    在分析了所有受训者编写的代码之后,我们总结了50个最常见的错误!更糟糕的是,每个小组所犯的错误与前一组的错误几乎一模一样。

    以下是 Rails 新手常常忽略或做错的地方。我们还包含了“对“,”错”两个版本的代码样本,使得教程更为清楚。

    1、他们不使用自动生成的方法

    ############
    ## WRONG ##
    ############  
    
    if course.visible    
      # do something
      end
      
    ##############
    ##  RIGHT   ##
    ##############  
    
    if course.visible?    
      # do something
      end
    

    通常,Rails 和许多 gems 会为它们使用的对象添加一些有用的帮助方法。例如,Rails 会自动为布尔字段添加声明。通常,这些方法的名字是以问号结尾的。请牢记这一点!

    2、他们不知道“N+1”查询来自何处

    #############
    ##  WRONG  ##
    #############  
    
     @homeworks = lesson.homeworks
    
      - @homeworks.each do |homework|
        %p homework.user.email
        
    #############
    ##  RIGHT  ##
    #############  
    
      @homeworks = lesson.homeworks.includes(:user)
    
      - @homeworks.each do |homework|
        %p homework.user.email
    

    了解 ORM 如何与数据库交互是非常重要的。但是,新手往往没有这种了解。因此,他们很少使用 “includes”、“preload” 与 “eager_load” 这类方法,并且对 “bullet” gem 一无所知。

    在第一个例子中,N+1 查询会传递至数据库。”N” 是已经完成的家庭作业数量。查询数量可能是10、20甚至100。而在第二个例子中,只有2个查询!

    3、他们不用 scopes(域)

    ############
    ## WRONG  ##
    ############  
    
    def index    
      @lessons = Сourse.lessons.order(position: :asc)  
    end
    
    ############
    ##  RIGHT ##
    ############  
    
    class Lesson < ActiveRecord::Base
        belongs_to :course
    
        scope :by_position, -> { order(position: :asc) }  
     end  
     
     def index    
       @lessons = course.lessons.by_position  
     end
    

    Scopes 允许你隐藏数据库的实现,并将代码唯一化(uniqualize)。而且,代码的可读性也会大幅提升,因为他们透露了开发者的意图,而非数据库的结构。

    4、他们不了解 “after_create” 与 “after_commit” 间的差别

    模型的数据,包括其在 “after_create” 中的新 ID,可以从内部,而非外部进行读取,原因是交易尚未完成。

    如果我在数据库中创建了一条记录,之后打算将其 ID 放入 redis 或任意的存储中,会得到以下结果:

    • 如果 ID 在交易完成之前使用,“after_create” 可能会导致无效数据。

    • 借助 “Sidekiq” 或其他任意后台工作,我总是可以使用 “after_commit” 确保数据的完整性。

    5、他们总是使用 ORM

    #############
    ##  WRONG  ##
    #############
    
      Article.all.each { |article| article.delete }
    
      Article.all.map { |article| article.title }
    
      Course.all.select { |course| course.created_at < 5.years.ago }.each { |course| course.articles.delete_all }
      
      #############
      ##  RIGHT  ##
      #############
    
      Article.delete_all
    
      Article.pluck(:title)
    
      old_courses_ids = Course.where(‘created_at < ?’, 5.years.ago’).pluck(:id)
      Article.where(course_id: old_courses_ids).delete_all
    

    尽管使用对象无疑非常方便,但整个过程却非常缓慢,而且需要很多内存。新手们可能并不理解代码的工作原理,以及如何提高其效率。

    6、他们不了解 “dependent destroy” 与 “delete_all” 的区别

    在被移除之前,“dependent destroy” 会选择所有受限记录,建立其对象,并调用各自的毁灭方法。此方法允许你移除所有受限数据。但是,当涉及大量数据时,这种方法就不管用了。

    至于 “dependent delete_all”,它会通过一条 SQL 查询移除自己。它效率很高,但是,在这种情况下,你得自己考虑数据库的完整性。

    7、他们不用带 bang 的方法

    #############
    ##  WRONG  ##
    #############
    
      class Article
        validates :body, length: { minimum: 200 }  
      end
    
      articles_data.each do |article_data|
        Article.create(article_data)  
      end
        
    #############
    ##  RIGHT  ##
    #############  
    
    # There are 2 possible solutions
    
      articles_data.each do |article_data|
        Article.create!(article_data)  
      end  
      
      # In this case a developer will be able to see that data he was not expencting to receive will get on the input
    
      articles_data.each do |article_data|
        article = Article.new(article_data)
    
        unless article.save
          puts ‘Can not save article’      
          #process this situation    
         end   
       end  
    # Give a user a choice.
    

    根据协议,将 bang(!) 添加至方法名的情况有如下两种:

    • 如果某个方法修改了其访问的对象

    • 如果某个方法在执行失败后抛出了异常

    新手们常常忽略第二种情况。如果代码出了问题,你必须尽快找到问题根源。例如,如果完全不处理将记录保存至数据库的结果,最好还是抛出异常以找到哪段代码处理了无效数据。

    在上例中,如果一个无效的物品传给输入,就会被忽视。

    8、他们不在迁移中设置默认字段

    #############
    ##  WRONG  ##
    #############  
    
    class Article
        after_initialize :set_default_status    
        
        def set_default_status      
          self.status = ‘pending’    
            end  
          end
    
          
    #############
    ##  RIGHT  ##
    #############  
    
    class MyMigration    
       def up
          change_column :articles, status, :string, default: ‘pending’    
        end    
        
      def down
          change_column :articles, status, :string    
        end  
      end
    

    如果字段中的某个模型必须要有一个默认值,应该通过数据库进行安装。

    9、他们不在迁移中设置限制条件

    #############
    ##  WRONG  ##
    #############  
    
    class MyMigration    
      def change
          add_column :profiles, user_id, :integer    
        end  
      end
      
      
    #############
    ##  RIGHT  ##
    #############  
    
    class MyMigration    
    def change
          add_column :profiles, user_id, :integer, null: false   
       end  
     end
    

    对于基础架构的限制条件越多,我们的应用就会越可靠。此外,别忘记 “null:false”,用户不可以没有简介。

    10、他们不在迁移中写反向迁移

    如果不能回滚,迁移的意义在哪儿?

    以上是新手们最常犯的 Ruby on Rails 错误的第一部分,如果喜欢本文,请记得分享哦。

    未完待续……

    本文系 OneAPM 工程师编译整理。OneAPM 能为您提供端到端的 Ruby 应用性能解决方案,我们支持所有常见的 Ruby 框架及应用服务器,助您快速发现系统瓶颈,定位异常根本原因。分钟级部署,即刻体验,Ruby 监控从来没有如此简单。想阅读更多技术文章,请访问 OneAPM 官方技术博客

    本文转自 OneAPM 官方博客

    原文地址:http://jetruby.com/expertise/common-ruby-rails-mistakes-beginners-make-model-database/

  • 相关阅读:
    Atitit.加密算法ati Aes的框架设计
    Atitit.加密算法ati Aes的框架设计
    Atitit.分布式远程调用  rpc  rmi  CORBA的关系
    Atitit.分布式远程调用  rpc  rmi  CORBA的关系
    Atitit.事件机制 与 消息机制的联系与区别
    Atitit.事件机制 与 消息机制的联系与区别
    Atitit  godaddy 文件权限 root权限设置
    Atitit  godaddy 文件权限 root权限设置
    Atitit.atiRI  与 远程调用的理论and 设计
    Atitit.atiRI  与 远程调用的理论and 设计
  • 原文地址:https://www.cnblogs.com/oneapm/p/5626116.html
Copyright © 2020-2023  润新知