专攻难题:Rails、MVC及最常用的Rails命令

在使用Ruby编写web应用程序时,创始人David HeinemeierHansson说,他只不过是将以往应用程序的通用部分复制粘贴到新程序中。

包括他在内,所有程序员都喜欢解决难题。尝试解决工作领域内独有难题的唯一方法,是构建一个抽象层,并清除脑海中所有关于基础web程序的细节。使用Rails可以专注解决困难部分,消减重复性工作。Rails命令十分神奇,但我们必须了解每个命令的功能和编写过程。

Rails是什么?

Rails是一种web应用框架。根据模型-视图-控制器(MVC)模式,它包括了创建数据库支持的web应用程序时所需的所有内容。MVC将应用分为三层:模型、视图和控制器,每一层都有特定任务。

模型-视图-控制器(MVC)结构示意图

模型层

模型层代表领域模型(如Account、Product、Person、Post等),概述应用程序中特有的业务逻辑。

在Rails中,数据库支持的模型类源于 ActiveRecord::Base。Active Record支持用户将数据库行的数据表示为对象,并使用业务逻辑方法修饰这些数据对象。大多数Rails模型由数据库支撑,模型也可以是Ruby普通类或实现由 Active Model模块提供的一组接口的Ruby类。

控制层

控制层的功能在于处理传入的HTTP请求以及给出适当响应。通常这一层意味着返回HTML,但Rails控制器还可以生成XML、JSON、PDF、移动专用视图及其他格式。

控制器加载和操作模型,并呈现视图模板,以便生成适当的HTTP响应。在Rails中,传入要求由Action Dispatch发送至相应控制器,控制类来自 ActionController::Base。Action Dispatch和Action Controller一起绑定于Action Pack中。

图源:unsplash

视图层

视图层由“模板”组成,主要负责提供应用程序资源的适当描述。模板可以有多种格式,但大多数视图模板都是带有内嵌Ruby代码的HTML(ERB 文件)。视图通常生成控制器响应,或生成电子邮件正文。在Rails中,视图生成由Action View处理。

开发一款新型Rails应用

首先从头构建一个应用程序,这步之前要确保系统内装有Ruby、RubyGems和Bundler。打开终端导航到一个有权限创建文件的目录。先要做的是安装Rails并通过运行railsnew命令构建一个新型Rails应用程序。

要从头开始创建只支持API的Rails,创建时--api应在Rails应用程序名称之后:railsnew --database=postgresql --api (使用Postgresql数据库配置应用程序更为容易,但别忘了在运行命令前安装这一数据库)。例如:rails new Gallery --database=postgresql –api。

通过使用 --api 旗标,Rails能够删除许多默认功能和中间件(大多是涉及无用的浏览器)。控制器继承ActionController::API而非ActionController::Base,生成器会跳过生成视图。为只支持API的Rails构建设置资源无须作任何改变。

将所有所需gem添加至Gemfile中后开始运行bundle install。

· 初始化数据库:rails db:create

· 运行Rails服务器:rails s

Rails生成

Rails团队的首要目标是高效地构建核心应用程序功能。Rails系统拥有大量生成器,能帮助用户完成一些手动作业。生成器除了节省时间,还有一些其他优点:

· 生成器能够为应用程序测试套件设置基本规格。它们会为用户提供一些基本例子而非编写复杂的逻辑测试。

· 经设置,生成器的运行方式保持不变,这样便能标准化代码并帮助用户更有效地开发,原因在于用户不必过多担心手写代码时可能会出现的拼写、语法错误或其他相关漏洞。

· 生成器遵循Rails最佳操作,包括使用RESTful命名模式、删除重复代码、使用部分和许多其他最佳类型设计模式。

然而,某些生成器会创建相当多的代码。如果不使用某个代码,它就会造成应用程序代码不必要的混乱,从而给以后的程序员带来困扰。所以何时使用生成器最好呢?所有Rails生成器作为命令输入终端,并遵循下列语法:

rails generate --no-test-framework

--no-test-framework 是一个旗标,意在提醒生成器不要为新生成模型和控制器等创建任何测试。当用户运行自己的Rails应用程序时则不需要旗标,这十分有助于快速建立测试套件。

为了提高效率,Rails将generate 方法简化为g,因此上述CLI命令可以简化为:

rails g --no-test-framework

图源:unsplash

下列是Rails提供的主要生成器。笔者将分别举例说明:

迁移生成器

Rails有一组遵守规范的优质迁移生成器,能够有效管理数据库模式。绘制域模型后可以进行下一步。首先使用案例库应用程序中的数据库迁移并更新 painters 表。运用下列命令在表中添加一列,将其命名为portrait:

railsg migration add_portrait_to_painters portrait:string --no-test-framework

终端显示创建了迁移文件20200928055457_add_portrait_to_painters.rb.。由于迁移文件名不得重复,生成器会在文件名前加上时间戳。神奇的事情发生了——打开db/migrat 目录中创建的文件,它现在应该是这样:

class AddPortraitToPainters < ActiveRecord::Migration[6.0]

def change

add_column :painters, :portrait,:string

end

end

注意到生成器的作用了吗?它自动感知到用户想添加一个新的列,构建了add_column 方法调用。这是如何做到的?

实践证明,命名迁移文件的方法至关重要。通过在名称前加上add_文本,它向迁移生成器发送了一个信号,即这种模式更改的目的是向表中添加一列/多列。

那么它是如何知道用户想在哪个表上添加的呢?它将_painters 文本附加到迁移名称的末尾,以此告诉Rails用户想更改的表格是 painters 表。最后,通过在命令末尾添加 portrait:string 文本告知生成器新的列的名称为portrait,数据类型为 string。

用户可以通过运行rails db:migrate 更新数据库模式,该模式也会反映出这次变更。

删除列则可以运行另外一个迁移:

railsg migration remove_portrait_from_painters portrait:string

如果打开此迁移文件则会看到下列代码:

class RemovePortraitToPainters < ActiveRecord::Migration[6.0]

def change

remove_column :painters, :portrait,:string

end

end

运行rails db:migrate 后,模式也会得到更新。当需要运行任何未决迁移时可以使用这一命令。

· railsdb:migrate:reset:它会删除数据库信息,在新的数据库运行迁移并重新加载种子数据。

· railsdb:seed:将数据从文件:db/seeds.rb加载到数据库中。这种方法对用Rails项目所需的初始数据填充数据库十分有用。

· railsdb:rollback:回滚上次执行的迁移。它会撤销上一次迁移,接着用户便可编辑文件并重新运行 rails db:migrate 。注意,进行过程中谨慎使用这一命令,因为它极具破坏性,很有可能造成数据丢失。运行时一定确保自己完全清楚运行后的结果。

模型生成器

这是一种经常使用的生成器类型。它适用于编写创建模型和关联数据库表所需的核心代码,且不会使应用程序过于复杂。一般来说需要模型名称(单数和首字母大写)和模型参数。

接下来为带有 name、bio和 genre列的Painter 应用程序添加一个新模型,可以通过以下CLI命令使用模型生成器:

railsg model Painter name:string genre:string bio:text

如果数据类型为字符串,则不需要在列名后指定类型,因为它是默认数据类型。因此如下:

railsg model Painter name genre bio:text

因其水平较高,这一步已经创建了:

· 添加一个表格和name、genre和 bio列的数据库迁移

· 继承ApplicationRecord (自Rails 5起)的模型文件

我们接下来创建另一个属于Painter的模型Painting:

railsg model Painting name image painter:belongs_to

切记首先生成具有has_many宏命令的代码;在运行迁移时只有带有belongs_to 的代码有用,否则会得到错误代码:

ActiveRecord::StatementInvalid:PG::UndefinedTable: ERROR: relation "" doesnot exist

运行这一生成器会创建出一个迁移文件和 Painter 和Painting模型。这一特定的生成器创建了一些具有单命令的不同的功能,且仅用最简单的代码就生成出来。接着转向自己的模型,确保建立适当关系(has_many, belongs_to)。

下一步是创建一个迁移:rails db:create 和 rails db:migrate来迁移表格。用户可以在控制台中测试连接,只需要运行rails c并创建一些新的例子,不论正确与否都可以测试验证;接下来测试关联度(如Painter.all.first.painting能够检查第一个painter的painting)

图源:unsplash

控制生成器

运行rails g controller 命令会提供一个控制文件(用于添加适当操作)和相应的视图文件夹:

railsg controller paintings

资源生成器

如果用户在在创建API过程中使用前端MVC架构,或只想手动创建视图,resource生成器是编写代码的一个优选。创建Painting资源:

railsg resource Painting name image painter:belongs_to

因为没有为name和image 编写数据类型,因此默认情况下它是一个字符串。添加 painter:belongs_to 来指定两个表格的关系,并在paintings表格中设置一列 painter_id。也可以使用 references编写上述命令(结果一样):

railsg resource Painting name image painter:references

这是为不同模型编写相同命令的另一个例子:

railsg resource Answer content correct_answer:boolean question:belongs_to

凭借生成器,目前的应用程序拥有什么?

· 一个迁移文件,能够在生成器中为传递给它的属性创建一个新的数据库表

· 继承ApplicationRecord的模型文件(从Rails 5开始,见上述注意事项)

· 继承了ApplicationController的控制器文件

· 不带有视图模板文件的视图目录

· 视图helper文件

· 用于该控制器的特定JavaScripts的Coffeescript文件

· 用于控制器样式的 scss文件

· routes.rb文件内的完整资源调用

resource生成器是一种智能生成器,创建全功能资源所需的一些核心功能,同时不会导致过多代码膨胀。

路由

图源:unsplash

最后一项可能不太常见。通常resources:paintings被视作为一个“魔法”路由,它包含在应用程序中执行CRUD所需的全部RESTful路由。那么resources:paintings会转化成什么?用滤波器运行 rake routes 会比较简单,这样就只会呈现paintings的路由:

rake routes | grep painting

resources自动创建每个路由并使其对控制器可用。打开paintings_controller.rb文件会很有趣:路由列表中出现的操作都不存在。它创建的代码非常少,只需添加应用程序需要的功能。

Scaffold生成器

相关推荐