如何构建一个Ruby on Rails应用程序

引言

Rails 是一个用Ruby编写的Web应用程序框架。它采取了一种有主见的开发方法,假设在有共同目标的情况下,设定的惯例最能服务开发者。因此,Rails提供了处理路由、状态数据、资产管理等方面的约定,以提供大多数Web应用程序所需的基础功能。

Rails遵循模型-视图-控制器(MVC)架构模式,该模式将应用程序的逻辑(位于模型中)与信息的处理和展示分离开来。这种组织结构——以及其他允许开发者将代码提取到助手局部视图的约定——确保了应用程序代码不会不必要的重复

在本教程中,您将构建一个Rails应用程序,使用户能够发布有关鲨鱼及其行为的信息。这将是一个未来应用开发的良好起点。

前提条件

要学习本教程,您需要:

  • 一台运行Ubuntu 18.04的本地机器或开发服务器。您的开发机器应具有拥有管理权限的非root用户,并配置了ufw防火墙。有关如何设置此配置的说明,请参阅我们的Ubuntu 18.04初始服务器设置教程。
  • 在您的本地机器或开发服务器上安装了Node.jsnpm。本教程使用Node.js版本10.16.3和npm版本6.9.0。有关在Ubuntu 18.04上安装Node.js和npm的指导,请遵循如何在Ubuntu 18.04上安装Node.js中“使用PPA安装”部分的说明。
  • 在您的本地机器或开发服务器上安装 Ruby、rbenv 和 Rails,按照 如何在 Ubuntu 18.04 上使用 rbenv 安装 Ruby on Rails 中的步骤 1-4 进行操作。本教程使用 Ruby 2.5.1、rbenv 1.1.2 和 Rails 5.2.0

步骤 1 — 安装 SQLite3

在创建我们的 Rails shark 应用程序之前,我们需要确保有一个数据库来存储用户数据。Rails 默认配置为使用 SQLite,这在开发中通常是一个不错的选择。由于我们的应用程序数据不需要高级别的编程扩展性,SQLite 将满足我们的需求。

首先,更新您的包索引:

  1. sudo apt update

接下来,安装 sqlite3libsqlite3-dev 包:

sudo apt install sqlite3 libsqlite3-dev

这将安装 SQLite 及其所需的开发文件。

检查您的版本以确认安装成功:

  1. sqlite3 --version
Output
3.22.0 2018-01-22 18:45:57 0c55d179733b46d8d0ba4d88e01a25e10677046ee3da1d5b1581e86726f2alt1

安装好 SQLite 后,您就可以开始开发您的应用程序了。

第二步 —— 创建一个新的 Rails 项目

安装好数据库后,我们可以创建一个新的 Rails 项目,并查看 Rails 通过 rails new 命令为我们提供的默认样板代码。

使用以下命令创建一个名为 sharkapp 的项目:

  1. rails new sharkapp

你将看到许多输出信息,告诉你 Rails 正在为你的新项目创建什么。下面的输出突出了一些重要的文件、目录和命令:

Output
create . . . create Gemfile . . . create app . . . create app/controllers/application_controller.rb . . . create app/models/application_record.rb . . . create app/views/layouts/application.html.erb . . . create config create config/routes.rb create config/application.rb . . . create config/environments create config/environments/development.rb create config/environments/production.rb create config/environments/test.rb . . . create config/database.yml create db create db/seeds.rb . . . run bundle install . . . Bundle complete! 18 Gemfile dependencies, 78 gems now installed. Use `bundle info [gemname]` to see where a bundled gem is installed. . . . * bin/rake: Spring inserted * bin/rails: Spring inserted

这里高亮的输出告诉你,Rails 已经创建了以下内容:

  • Gemfile:这个文件列出了你应用程序的 gem 依赖项。一个 gem 是一个 Ruby 软件包,而 Gemfile 允许你管理项目的软件需求。
  • appapp 目录是你主要应用程序代码的所在地。这包括构成应用程序本身的模型、控制器、视图、资产、辅助器和邮件程序。Rails 为你提供了一些应用程序级别的样板代码,用于 MVC 模型,例如 app/models/application_record.rbapp/controllers/application_controller.rbapp/views/layouts/application.html.erb
  • config:此目录包含应用程序的配置设置:
    • config/routes.rb:应用程序的路由声明存放在此文件中。
    • config/application.rb:应用程序各组件的通用设置位于此文件内。
  • config/environments:此目录存放各环境的配置设置。Rails 默认包含三种环境:development(开发环境)、production(生产环境)和 test(测试环境)。
  • config/database.yml:数据库配置设置位于此文件中,分为四个部分:default(默认)、development(开发)、production(生产)和 test(测试)。得益于 rails new 命令附带的 Gemfile 中包含了 sqlite3 gem,我们的 config/database.yml 文件已将 adapter 参数设置为 sqlite3,指定我们将使用 SQLite 数据库来运行此应用程序。
  • db:此文件夹包含一个名为migrate的数据库迁移目录,以及schema.rbseeds.rb文件。schema.rb包含有关数据库的信息,而seeds.rb则是存放数据库种子数据的地方。

最后,Rails会运行bundle install命令来安装Gemfile中列出的依赖项。

一切准备就绪后,导航至sharkapp目录:

  1. cd sharkapp

现在,您可以使用rails server命令启动Rails服务器,以确保应用程序正常运行。如果您在本地机器上操作,请输入:

  1. rails server

Rails默认绑定到localhost,因此您现在可以通过在浏览器中导航至localhost:3000来访问您的应用程序,您将看到以下图像:

如果您在开发服务器上工作,首先确保允许在端口3000上进行连接:

  1. sudo ufw allow 3000

然后使用--binding标志启动服务器,绑定到您的服务器IP:

  1. rails server --binding=your_server_ip

在浏览器中导航至http://your_server_ip:3000,您将看到Rails的欢迎信息。

一旦你浏览完毕,可以通过按下 CTRL+C 来停止服务器。

应用创建并就位后,你就可以从 Rails 样板代码开始,构建一个独特的应用了。

第三步 — 搭建应用脚手架

为了创建我们的鲨鱼信息应用,我们需要创建一个模型来管理应用数据,视图来允许用户与这些数据交互,以及一个控制器来管理模型和视图之间的通信。为了构建这些组件,我们将使用 rails generate scaffold 命令,该命令将为我们提供一个模型、一个用于修改数据库模式的数据库迁移、一个控制器、一套完整的视图来管理应用的创建、读取、更新和删除(CRUD)操作,以及部分模板、助手和测试。

由于 generate scaffold 命令为我们做了大量工作,我们将仔细查看它创建的资源,以理解 Rails 在幕后所做的工作。

我们的 generate scaffold 命令将包含我们模型的名称以及我们希望数据库表中包含的字段。Rails 使用 Active Record 来管理应用程序数据(构建为具有模型的对象)与应用程序数据库之间的关系。我们的每个模型都是一个 Ruby 类,同时也继承自 ActiveRecord::Base 类。这意味着我们可以像操作 Ruby 类一样操作我们的模型类,同时还能引入 Active Record 的方法。Active Record 随后会确保每个类映射到数据库中的一个表,并且该类的每个实例映射到该表中的一行。

输入以下命令以生成一个 Shark 模型、控制器和相关视图:

  1. rails generate scaffold Shark name:string facts:text

通过 name:stringfacts:text,我们向 Rails 提供了关于我们希望数据库表中包含的字段及其应接受的数据类型的信息。两者都为我们提供了输入所需内容的余地,尽管 text 允许输入更多字符用于鲨鱼事实。

当你输入此命令时,你将再次看到一长串输出,解释 Rails 为你生成的所有内容。下面的输出突出了对我们设置较为重要的几个方面:

Output
invoke active_record create db/migrate/20190804181822_create_sharks.rb create app/models/shark.rb . . . invoke resource_route route resources :sharks invoke scaffold_controller create app/controllers/sharks_controller.rb invoke erb create app/views/sharks create app/views/sharks/index.html.erb create app/views/sharks/edit.html.erb create app/views/sharks/show.html.erb create app/views/sharks/new.html.erb create app/views/sharks/_form.html.erb . . .

Rails 已经创建了模型文件 app/models/shark.rb 以及相应的数据库迁移文件:db/migrate/20190804181822_create_sharks.rb。您的迁移文件上的时间戳将与这里显示的不同。

同时,它还生成了控制器 app/controllers/sharks_controller.rb,以及与应用程序 CRUD 操作相关的视图,这些视图被收集在 app/views/sharks 目录下。在这些视图中,包含了一个部分视图 _form.html.erb,该视图包含了跨多个视图使用的代码。

最后,Rails 在 config/routes.rb 中添加了一条新的资源路由 resources :sharks,这使得 Rails 路由器能够将传入的 HTTP 请求与 sharks 控制器及其相关视图进行匹配。

尽管 Rails 已经为我们完成了应用程序代码的大部分构建工作,但查看一些文件以理解正在发生的事情仍然是有价值的。

首先,让我们通过以下命令查看控制器文件:

  1. cat app/controllers/sharks_controller.rb
Output
class SharksController < ApplicationController before_action :set_shark, only: [:show, :edit, :update, :destroy] # GET /sharks # GET /sharks.json def index @sharks = Shark.all end # GET /sharks/1 # GET /sharks/1.json def show end # GET /sharks/new def new @shark = Shark.new end # GET /sharks/1/edit def edit end # POST /sharks # POST /sharks.json def create @shark = Shark.new(shark_params) respond_to do |format| if @shark.save format.html { redirect_to @shark, notice: 'Shark was successfully created.' } format.json { render :show, status: :created, location: @shark } else format.html { render :new } format.json { render json: @shark.errors, status: :unprocessable_entity } end end end # PATCH/PUT /sharks/1 # PATCH/PUT /sharks/1.json def update respond_to do |format| if @shark.update(shark_params) format.html { redirect_to @shark, notice: 'Shark was successfully updated.' } format.json { render :show, status: :ok, location: @shark } else format.html { render :edit } format.json { render json: @shark.errors, status: :unprocessable_entity } end end end # DELETE /sharks/1 # DELETE /sharks/1.json def destroy @shark.destroy respond_to do |format| format.html { redirect_to sharks_url, notice: 'Shark was successfully destroyed.' } format.json { head :no_content } end end private # 使用回调函数在动作间共享通用设置或约束。 def set_shark @shark = Shark.find(params[:id]) end # 永远不要信任来自可怕互联网的参数,只允许白名单中的参数通过。 def shark_params params.require(:shark).permit(:name, :facts) end end

控制器负责管理信息如何被获取并传递给其关联的模型,以及如何与特定的视图相关联。如你所见,我们的sharks控制器包含了一系列方法,这些方法大致映射到标准的CRUD操作。然而,为了在出错时提高效率,这些方法比CRUD功能更多。

例如,考虑create方法:

~/sharkapp/app/controllers/sharks_controller.rb
. . .
  def create
    @shark = Shark.new(shark_params)

    respond_to do |format|
      if @shark.save
        format.html { redirect_to @shark, notice: 'Shark was successfully created.' }
        format.json { render :show, status: :created, location: @shark }
      else
        format.html { render :new }
        format.json { render json: @shark.errors, status: :unprocessable_entity }
      end
    end
  end
. . . 

如果Shark类的一个新实例成功保存,redirect_to将生成一个新的请求,该请求随后被导向控制器。这将是一个GET请求,并由show方法处理,向用户展示他们刚刚添加的鲨鱼。

如果保存失败,Rails将再次渲染app/views/sharks/new.html.erb模板,而不是向路由器发出另一个请求,从而给用户再次提交数据的机会。

除了鲨鱼控制器之外,Rails还为我们提供了一个index视图模板,该视图对应于控制器中的index方法。我们将使用这个视图作为应用程序的根视图,因此值得一看。

输入以下命令以输出该文件:

  1. cat app/views/sharks/index.html.erb
Output
<p id="notice"><%= notice %></p> <h1>Sharks</h1> <table> <thead> <tr> <th>Name</th> <th>Facts</th> <th colspan="3"></th> </tr> </thead> <tbody> <% @sharks.each do |shark| %> <tr> <td><%= shark.name %></td> <td><%= shark.facts %></td> <td><%= link_to 'Show', shark %></td> <td><%= link_to 'Edit', edit_shark_path(shark) %></td> <td><%= link_to 'Destroy', shark, method: :delete, data: { confirm: 'Are you sure?' } %></td> </tr> <% end %> </tbody> </table> <br> <%= link_to 'New Shark', new_shark_path %>

index视图会遍历我们的Shark类实例,这些实例已映射到数据库中的sharks表。通过ERB模板,视图输出与每个鲨鱼实例关联的表中的各个字段:namefacts

视图随后使用link_to助手创建一个超链接,提供的字符串作为链接文本,提供的路径作为目标地址。这些路径的实现得益于我们在使用rails generate scaffold命令定义sharks资源路由时可用的助手

除了查看index视图,我们还可以查看new视图,了解Rails如何在视图中使用局部模板。输入以下命令以输出app/views/sharks/new.html.erb模板:

  1. cat app/views/sharks/new.html.erb
Output
<h1>New Shark</h1> <%= render 'form', shark: @shark %> <%= link_to 'Back', sharks_path %>

尽管此模板看似缺少新建鲨鱼条目的输入字段,但提及的 `render 'form'` 指示我们,模板正在引入 `_form.html.erb` 局部视图,该局部视图提取了跨视图重复的代码片段。

查阅该文件将使我们全面了解如何创建新的鲨鱼实例:

  1. cat app/views/sharks/_form.html.erb
Output
<%= form_with(model: shark, local: true) do |form| %> <% if shark.errors.any? %> <div id="error_explanation"> <h2><%= pluralize(shark.errors.count, "error") %> prohibited this shark from being saved:</h2> <ul> <% shark.errors.full_messages.each do |message| %> <li><%= message %></li> <% end %> </ul> </div> <% end %> <div class="field"> <%= form.label :name %> <%= form.text_field :name %> </div> <div class="field"> <%= form.label :facts %> <%= form.text_area :facts %> </div> <div class="actions"> <%= form.submit %> </div> <% end %>

此模板采用了 `form_with` 表单辅助方法。表单辅助方法旨在利用特定模型的字段和作用域,简化从用户输入创建新对象的过程。在此,`form_with` 以 `model: shark` 为参数,其创建的新表单构建器对象包含与 `sharks` 表字段相对应的输入字段。因此,用户拥有输入鲨鱼 `name` 和鲨鱼 `facts` 的表单字段。

提交此表单将生成包含用户数据的 JSON 响应,应用程序其余部分可通过 params 方法 访问这些数据,该方法会创建一个包含该数据的 `ActionController::Parameters` 对象。

既然您已了解 `rails generate scaffold` 为您生成的内容,接下来可以继续为应用程序设置根视图。

步骤 4 — 创建应用根视图并测试功能

理想情况下,您希望应用程序的着陆页映射到应用程序的根目录,以便用户能够立即了解应用程序的用途。

处理此问题有多种方法:例如,您可以创建一个 Welcome 控制器和一个相关的 index 视图,这将给用户一个通用的着陆页,同时还可以链接到应用程序的不同部分。然而,在本例中,让用户直接进入我们的 index 鲨鱼视图足以作为应用程序用途的介绍。

为了实现这一点,您需要在 config/routes.rb 中修改路由设置,以指定应用程序的根路径。

使用 nano 或您喜欢的编辑器打开 config/routes.rb 进行编辑:

  1. nano config/routes.rb

文件内容如下:

~/sharkapp/config/routes.rb
Rails.application.routes.draw do
  resources :sharks
  # 有关此文件中可用的 DSL 的详细信息,请参阅 http://guides.rubyonrails.org/routing.html
end

如果没有设置更具体的内容,默认视图在 http://localhost:3000http://your_server_ip:3000 将是默认的 Rails 欢迎页面。

为了将应用程序的根视图映射到鲨鱼控制器的`index`视图,您需要在文件中添加以下行:

~/sharkapp/config/routes.rb
Rails.application.routes.draw do
  resources :sharks

  root 'sharks#index' 
  # 有关此文件中可用的DSL的详细信息,请参阅http://guides.rubyonrails.org/routing.html
end

现在,当用户导航到您的应用程序根目录时,他们将看到一个完整的鲨鱼列表,并有机会创建新的鲨鱼条目,查看现有条目,以及编辑或删除特定条目。

完成编辑后,保存文件并退出编辑器。如果您使用`nano`编辑文件,可以通过按`CTRL+X`,`Y`,然后按`ENTER`来完成操作。

现在,您可以使用以下命令运行迁移:

  1. rails db:migrate

您将看到确认迁移的输出。

再次启动您的Rails服务器。如果您在本地工作,请输入:

  1. rails s

在开发服务器上,输入:

  1. rails s --binding=your_server_ip

如果您在本地工作,请导航到`localhost:3000`,如果在开发服务器上工作,请导航到`http://your_server_ip:3000`。

您的应用程序登录页面将如下所示:

要创建新的鲨鱼,请点击页面底部的New Shark链接,这将带您到`sharks/new`路由:

让我们添加一些演示信息来测试我们的应用程序。在Name字段中输入“Great White”,在Facts字段中输入“Scary”:

点击Create按钮以创建鲨鱼。

这将引导你进入 show 路由,得益于 before_action 过滤器,它通过 set_shark 方法设置,该方法获取了我们刚刚创建的鲨鱼的 id

~/sharkapp/app/controllers/sharks_controller.rb
class SharksController < ApplicationController
  before_action :set_shark, only: [:show, :edit, :update, :destroy]

  . . . 

  def show
  end

  . . . 

  private
    # 使用回调在动作间共享通用设置或约束。
    def set_shark
      @shark = Shark.find(params[:id])
    end
  . . .

你现在可以通过点击鲨鱼条目上的 编辑 来测试编辑功能。这将带你进入该鲨鱼的 edit 路由:

将大白鲨的 facts 从“可怕”改为“巨大”,然后点击 更新鲨鱼。这将带你回到 show 路由:

最后,点击 返回 将带你到更新后的 index 视图:

现在你已经测试了应用的基本功能,可以添加一些验证和安全检查,使一切更加安全。

步骤 5 — 添加验证

你的鲨鱼应用可以接受用户输入,但设想一个用户试图创建一个没有添加事实的鲨鱼,或者为数据库中已有的鲨鱼创建条目。你可以通过向模型添加验证来创建检查数据的机制,以确保数据在进入数据库之前是有效的。由于应用的逻辑位于其模型中,在这里进行数据输入验证比在应用的其他地方更有意义。

请注意,本教程不会涉及编写验证测试的内容,但您可以通过查阅Rails 文档来了解更多关于测试的信息。

如果您尚未停止服务器,请继续输入CTRL+C来停止它。

打开您的shark.rb模型文件:

  1. nano app/models/shark.rb

当前,文件告诉我们Shark类继承自ApplicationRecord,而后者又继承自ActiveRecord::Base

~/sharkapp/app/models/shark.rb
class Shark < ApplicationRecord
end

首先,让我们为name字段添加一些验证,确保该字段被填写且条目是唯一的,以防止重复条目:

~/sharkapp/app/models/shark.rb
class Shark < ApplicationRecord
  validates :name, presence: true, uniqueness: true
end

接下来,为facts字段添加验证,确保它也被填写:

~/sharkapp/app/models/shark.rb
class Shark < ApplicationRecord
  validates :name, presence: true, uniqueness: true
  validates :facts, presence: true
end

我们在这里不太关心事实的唯一性,只要它们与唯一的鲨鱼条目相关联即可。

完成后保存并关闭文件。

再次启动服务器,使用rails srails s --binding=your_server_ip,取决于您是在本地工作还是在开发服务器上。

导航到您应用程序的根路径,地址为http://localhost:3000http://your_server_ip:3000

点击新建鲨鱼。在表单中,将“大白鲨”添加到名称字段,将“大牙齿”添加到特点字段,然后点击创建鲨鱼。您应该会看到以下警告:

现在,让我们看看是否能检查其他验证。点击返回回到主页,然后再次点击新建鲨鱼。在新表单中,在名称字段输入“虎鲨”,并保持特点字段为空。点击创建鲨鱼将触发以下警告:

通过这些更改,您的应用程序已设置了一些验证,以确保保存到数据库的数据一致性。现在,您可以将注意力转向应用程序的用户,并定义谁可以修改应用程序数据。

第六步 — 添加认证

有了验证措施,我们对保存到数据库的数据有了一定的保障。但用户呢?如果我们不希望任何用户都能向数据库添加数据,那么我们应该添加一些认证措施,确保只有授权用户才能添加鲨鱼。为此,我们将使用http_basic_authenticate_with方法,该方法允许我们创建用户名和密码组合来验证用户。

在 Rails 中,有多种方式可以对用户进行身份验证,包括使用 bcryptdevise 等 gem。然而,目前我们将在应用控制器中添加一个方法,该方法将适用于整个应用程序的各个动作。如果将来我们为应用程序添加更多控制器,这将非常有用。

再次使用 CTRL+C 停止服务器。

打开定义 ApplicationController 的文件:

  1. nano app/controllers/application_controller.rb

在其中,你会看到 ApplicationController 类的定义,应用程序中的其他控制器都继承自该类:

~/sharkapp/app/controllers/application_controller.rb
class ApplicationController < ActionController::Base
end

为了验证用户,我们将使用 http_basic_authenticate_with 方法,并硬编码用户名和密码。将以下代码添加到文件中:

~/sharkapp/app/controllers/application_controller.rb
class ApplicationController < ActionController::Base
  http_basic_authenticate_with name: 'sammy', password: 'shark', except: [:index, :show]
end

除了在此处提供用户名和密码外,我们还通过指定不需要进行身份验证的路由(indexshow)来限制身份验证。另一种实现方式是写成 only: [:create, :update, :destroy]。这样,所有用户都能查看所有的鲨鱼并阅读特定鲨鱼的事实。然而,在涉及到修改网站内容时,用户需要证明他们有权限。

在一个更健壮的设置中,您不会希望以这种方式硬编码值,但为了演示目的,这将使您了解如何为应用程序的路由包含身份验证。它还让您看到Rails默认情况下如何在cookies中存储会话数据:一旦您在指定的操作上进行身份验证,您在同一会话中将不再需要重新进行身份验证。

完成编辑后,保存并关闭app/controllers/application_controller.rb。现在,您可以测试身份验证的实际效果了。

使用rails srails s --binding=your_server_ip启动服务器,并导航到您的应用程序,网址可以是http://localhost:3000http://your_server_ip:3000

在登录页面上,点击New Shark按钮。这将触发以下身份验证窗口:

如果您输入添加到app/controllers/application_controller.rb中的用户名和密码组合,您将能够安全地创建一条新的鲨鱼记录。

现在,您拥有了一个功能齐全的鲨鱼应用程序,具备了数据验证和基本身份验证方案。

结论

您在本教程中创建的 Rails 应用程序是一个起点,您可以在此基础上进行进一步开发。如果您有兴趣探索 Rails 生态系统,项目文档是一个很好的起点。

您还可以通过阅读如何在 Ruby on Rails 应用程序中创建嵌套资源来了解更多关于向项目中添加嵌套资源的信息,这将指导您如何构建应用程序的模型和路由。

此外,您可能还想探索如何使用 React 等框架为您的项目设置更强大的前端。如何使用 React 前端设置 Ruby on Rails 项目提供了这方面的指导。

如果您想探索不同的数据库选项,还可以查看如何在Ubuntu 18.04上将PostgreSQL与您的Ruby on Rails应用程序一起使用,该指南详细介绍了如何使用PostgreSQL而不是SQLite进行工作。您还可以参考我们的PostgreSQL教程库,以了解更多关于使用此数据库的信息。

Source:
https://www.digitalocean.com/community/tutorials/how-to-build-a-ruby-on-rails-application