Rails [翻译] Rails 3.2 开发标准指南

xiongbo · 2013年01月28日 · 最后由 sevk 回复于 2013年02月07日 · 6430 次阅读

原文地址

注:我已经 fork 了一份,以后就在那里更新了貌似作者还在不停的添加新的内容

这是 git 上的一篇关于 rails 开发实践的指南,我尝试翻译了一下,但是还是有些地方不是很明白,希望借助大家的力量纠正其中的错误,并探讨其中的合理性。

翻译水平有限,还请大家多多指教!

Approach

使用途径

Apply the YAGNI and KISS principles to all of the following.

在以下实践中贯彻 YAGNIKISS 原则

  • General architecture
  • Product and API features
  • Implementation specifics

  • 一般架构

  • 产品及 API 功能

  • 实现细节

Files

文件

  • Spaces not tabs
  • tab = 2 spaces
  • Unix line endings
  • UTF-8 encoding

  • 采用空格作为缩进而不是制表符

  • 采用 2 个空格作为一个缩进

  • 采用 Unix 行尾结束符

  • 采用 UTF-8 编码格式

Documentation

文档

All classes, modules, and methods must be documented using YARD formatted comments.

使用 YARD 为所有的类、模块和方法格式化注释。

General Guidelines

一般准则

These guidelines are based on Sandy Metz's programming "rules" which she introduced on Ruby Rogues.

以下的准则均基于 Sandy Metz 在 Ruby Rogues 上所介绍的编程“规则”.

The rules are purposefully aggressive and are designed to give you pause so your app won't run amok. It's expected that you will break them for pragmatic reasons... alot. See the note on YAGNI and KISS.

这些规则是故意设计成具有强制性,就是为了通过适度的“中止”,以使你的应用不会失去控制。 希望你是在基于很多务实的原因才会打破它。参考YAGNI and KISS

  • Classes can be no longer than 100 lines of code.
  • Methods can be no longer than 5 lines of code.
  • Methods can take a maximum of 4 parameters.
  • Controllers should only instantiate 1 object.
  • Views should only have access to 1 instance variable.
  • Never directly reference another class/module from within a class. Such references should be passed in.

  • 类中的代码不超过 100 行

  • 方法中的代码不超过 5 行

  • 方法最多采用 4 个参数

  • 控制器应该只实例化一个对象

  • 视图应该只获取一个实例变量

  • 永远不要直接在类中直接引用其他的类或者模块 而应该传递这些引用

Models

模型

  • Never use dynamic finders. e.g. find_by_...
  • Be thoughtful about using callbacks. They can lead to unwanted coupling.

  • 永远不要使用动态的查找方法。例如:find_by_...

  • 仔细斟酌对回调的使用。他们会导致不必要的耦合。

All models should be organized using the following format.

所有的模型请采用以下的格式组织代码

class MyModel < ActiveRecord::Base
  # extends ...................................................................
  # includes ..................................................................
  # security (i.e. attr_accessible) ...........................................
  # relationships .............................................................
  # validations ...............................................................
  # callbacks .................................................................
  # scopes ....................................................................
  # additional config .........................................................
  # class methods .............................................................
  # public instance methods ...................................................
  # protected instance methods ................................................
  # private instance methods ..................................................
end

NOTE: The comments listed above should exist in the file to serve as a visual reminder of the format.

注意:以上所列出的注释需明确的编写在文件中,用于在视觉上给出必要的格式提示。

Model Implementation

模型的实现

Its generally a good idea to isolate different concerns into separate modules. We recommend using Concerns as outlined in this blog post.

针对不同的关注点进行模块分离通常是一个好主意。我们推荐从这篇博客中的介绍开始。

|-project
  |-app
    |-assets
    |-controllers
    |-helpers
    |-mailers
    |-models
      |-concerns <-----
    |-views

Guidelines

指导方针

  • CRUD operations that are limited to a single model should be implemented in the model. For example, a full_name method that concats first_name and last_name
  • CRUD operations that reach beyond this model should be implemented as a Concern. For example, a status method that needs to look at several other models to calculate.
  • Simple non-CRUD operations should be implemented as a Concern.
  • Important! Concerns should be isolated and self contained. They should NOT make assumptions about how the receiver is composed at runtime. It's unacceptable for a concern to invoke methods defined in other concerns; however, invoking methods defined in the intended receiver is permissible.
  • Complex multi-step operations should be implemented as a process. See below.

  • 限于单个模型的 CRUD 操作应该在模型内实现。比如,采用full_name方法合并first_namelast_name

  • 超出当前模型的 CRUD 操作应该作为一个关注点。比如,status方法就需要依据许多其他的模型进行计算。

  • 简单的非 CRUD 操作应该作为一个关注点实现。

  • 非常重要! 关注点应该独立而且自包含。他们应该假设接收者在运行时是如何组成的。一个关注去调用其他关注中的方法是不被接受的。然而,调用定义在预定接收者中的方法是允许的。

  • 复杂的 多步 操作应该作为过程实现。见下文

Processes

过程

A process is defined as a multi-step operation which includes any of the following.

过程是对“多步”操作的定义,它包含以下内容。

  • A complex task oriented transaction is being performed.
  • A call is made to an external service.
  • Any OS level interaction is performed.
  • Sending emails, exporting files, etc...

  • 需要执行复杂的、任务导向的事务。

  • 对外部服务的调用。

  • 执行任何操作系统级别的交互。

  • 发送邮件、导出文件,等等...。

In an attempt to better manage processes, we loosely follow some domain driven development (DDD) principles. Namely, we have added a processes directory under app to hold our process implementations.

为了更好的管理流程,我们自由(loosely)遵循一些领域驱动模型开发原则。即,我们在app目录下添加processes文件夹,用于支持(hold)我们对过程的实现。

|-project
  |-app
    |-assets
    |-controllers
    |-helpers
    |-mailers
    |-models
    |-processes <-----
    |-views

We recommend using a tool like Hero to help model these processes.

我们推荐使用类似Hero的工具来帮助我们对这些过程建模。

Important Do not use model or controller callbacks to invoke a process. Instead, invoke processes directly from the controller.

重要 不要通过模型或者控制器回调去调用过程。而应该取而代之的直接在控制器中调用。

Logging

日志

We use the Yell gem for logging. Here is an example configuration.

我们使用 Yell gem 进行日志记录。下面是一份配置范例。

# example/config/application.rb 
module Example
  class Application < Rails::Application
    log_levels = [:debug, :info, :warn, :error, :fatal]

    # %m : The message to be logged
    # %d : The ISO8601 Timestamp
    # %L : The log level, e.g INFO, WARN
    # %l : The log level (short), e.g. I, W
    # %p : The PID of the process from where the log event occured
    # %t : The Thread ID from where the log event occured
    # %h : The hostname of the machine from where the log event occured
    # %f : The filename from where the log event occured
    # %n : The line number of the file from where the log event occured
    # %F : The filename with path from where the log event occured
    # %M : The method where the log event occured
    log_format = Yell.format( "[%d] [%L] [%h][%p][%t] [%F:%n:%M] %m")

    config.logger = Yell.new do |logger|
      logger.adapter STDOUT, :level => log_levels, :format => log_format 
    end
  end
end

Extensions & Monkey Patches

扩展和猴子补丁

  • Be thoughtful about monkey patching and look for alternative solutions first.
  • Use an initializer to load extensions & monkey patches.

  • 仔细斟酌是否需要使用猴子补丁,首先应该考虑是否有能够替代的解决方案。

  • 使用初始化程序(initializer)载入扩展和猴子补丁。

All extensions & monkey patches should live under an extensions directory in lib.

所有的扩展和猴子补丁都应该存放在lib目录下的extensions文件夹中。

|-project
  |-app
  |-config
  |-db
  |-lib
    |-extensions <-----

Use modules to extend objects or add monkey patches. This provides some introspection assistance when you need to track down weirdness.

采用模块的方式扩展对象或添加猴子补丁。这能够在你跟踪异常的时候为你提供一些自省的帮助

Here's an example:

下面是一个例子:

module CowboyString
  def downcase
    self.upcase
  end
end
::String.send(:include, CowboyString)
String.ancestors # => [String, CowboyString, Enumerable, Comparable, Object, Kernel]

***********2013-01-29 更新****************

Gem Dependencies

Gem 依赖性

Gem dependencies should be hardened before deploying the application to production. This will ensure application stability.

应用所依赖的 Gem 版本在发布到生产环境的时候,必须固定。这样可以保证应用的可靠性。

We recommend using exact or tilde version specifiers. Here's an example.

我们推荐使用精确或波浪线版本的说明符。下面是一个例子。

# Gemfile
gem 'rails', '3.2.11' # GOOD: exact specifier
gem 'pg', '~>0.9'     # GOOD: tilde specifier
gem 'nokogiri'        # BAD: no version specifier

Bundler's Gemfile.lock solves the same problem; however, our team discovered that inadvertent bundle updates were causing dependency problems. It's much better to be explicit in the Gemfile and guarantee application stability.

*Bundler 所使用的 Gemfile.lock 文件也可以用来解决这个问题;但是,我们的小组发现如果不小心使用了bundle update 同样会导致依赖性问题。因此,为了保证应用的可靠性,在 Gemfile 中进行精确的定义还是最好的办法。

This will cause your project to slowy drift away from the bleeding edge. A strategy should be employed to ensure your project doesn't drift too far from contemporary gem versions. For example, we are vigilant about security patch upgrades and we periodically upgrade gems on a regular basis.

但是这可能会导致你的项目逐渐偏离前沿版本。 你必须采取一定的策略才能保证你不会偏得太远。 比如我们对安全补丁的升级保持关注,并周期性的进行常规升级。

A Note on Client Side Frameworks

关于客户端框架的注意事项

Exciting things are happening in the world of client side frameworks.

客户端框架的世界不断的有惊喜出现,它们包括:

Be thoughtful about the decision to use a client side framework. Ask yourself if the complexity of maintaining 2 independent full stacks is the right decision. Often times there are better and simpler solutions.

谨慎选择你所要使用的客户端框架。 请从自身考虑,维护 2 套独立完整的栈(stacks)是否是一个正确的决定。特别是通常都会有更好且更简单的解决方案。

Read the following articles before deciding. In the end, you should be able to articulate why your decision is the right one.

在做出决定前,请先阅读以下的文章。最后,你应该能够说清楚为什么你的决定是正确的。

In either case be mindful of "layout thrashing" as described here.

在以上的任何一种情况下都要留心“布局抖动”,可以参考这篇文章.中的描述。

非常赞,我觉得你可以直接在 Github 上 fork 一下,然后附加一个 .zh_cn.md 的中文版,然后我们去 fork 你。

好棒 让刚刚接触的也能有个认识

顺带顶楼上 :)

永远不要使用动态的查找方法。例如:find_by_... 我想了解一下这个,不用是避免注入吗? 使用where代替?

@lgn21st 这真是个好注意,我已经放在 github 上了,以后就在那里更新了

那个模型的组织代码结构真的不错啊。

关于这部分,顺便一问 acts_as_xxx 和定义的一些常量 应该算在哪一部分里面

@xiongbo @tiseheaini find_by 是原来 Rails 2 时代的 API 风格,跟 Rails 3 的查询 API 风格已经不同了,去掉也让人少点迷惑。而且 find_by 是通过 method_missing 执行的,效率比较低。感觉 Rails 随着 API 的规范化,在渐渐的减少 magic method 了。

不错的指南。

这个好像不好控制:

类中的代码不超过100行
方法中的代码不超过5行

#12 楼 @QueXuQ 结果制造了海量的类。。。如果一个文件中的类数再被控制下的话 那就是海量文件了。。这不是蛋疼嘛。。

如果能自动控制 类的结构和类的数量就好了。 或把每个类做成一个 gem,需要的时候再加载安装?

#13 楼 @iBachue 对呀。这样感觉要跑出很多文件,类阿。想类名都类死了

#14 楼 @sevk 鉴于目前广泛使用的 gembundler 恐怕是做不到了。。。

@sevk 拆成 gem 测试和维护也是个问题,测试只能靠 TDD,或者再建一个 example 项目看看显示效果。稍有改动就是版本号升级。。。

是的,有时候不需要很复杂的功能 都已经很先进了。

需要 登录 后方可回复, 如果你还没有账号请 注册新账号