Ruby Rails学习中:User 模型,验证用户数据
用户建模
一. User 模型
实现用户注册功能的第一步是,创建一个数据结构,用于存取用户的信息。
在 Rails 中,数据模型的默认数据结构叫模型(model,MVC 中的 M)。Rails 为解决数据持久化提供的默认解决方案是,使用数据库存储需要长期使用的数据。与数据库交互默认使用的是 ActiveRecord。Active Record 提供了一系列方法,无需使用关系数据库所用的结构化查询语言(Structured QueryLanguage,简称 SQL),就能创建、保存和查询数据对象。Rails 还支持迁移(migration)功能,允许我们使用纯 Ruby 代码定义数据结构,而不用学习 SQL 数据定义语言(Data Definition Language,简称 DDL)。最终的结果是,Active Record 把你和数据库完全隔开了。咱们开发的应用在本地使用 SQLite,部署后使用PostgreSQL。这就引出了一个更深层的话题——在不同的环境中,即便使用不同类型的数据库,我们也无需关心 Rails 是如何存储数据的。
1.数据库迁移
回顾一下前面的内容, 我们在自己创建的 User 类中为用户对象定义了 name 和 email 两个属性。那是个很有用的例子, 但没有实现持久化存储最关键的要求: 在 Rails 控制台中创建的用户对象, 退出控制台后就会消失。这次的目的是为用户创建一个模型, 让用户数据不会这么轻易消失。
与前面定义的 User 类一样, 我们先为 User 模型创建两个属性, 分别为 name 和 email 。我们会把 email 属性用作唯一的用户名。 (下面也会添加一个属性, 用于存储密码。)在前面的代码中,我们使用 Ruby的 attr_accessor 方法创建了这两个属性:
class User
attr_accessor :name, :email
...
end
不过, 在 Rails 中不用这样定义属性。前面提到过,Rails 默认使用关系数据库存储数据, 数据库中的表由数据行(row)组成, 每一行都有相应的列(column), 对应于数据属性。例如, 为了存储用户的名字和电子邮件地址, 我们要创建 users 表, 表中有两个列, name 和 email , 这样每一行就表示一个用户, 如下图所
示, 对应的数据模型如下图所示。(下图只是梗概, 完整的数据模型请往下看。)把列命名为 name 和 email 后, Active Record 会自动把它们识别为用户对象的属性。
注:你可能还记得, 在上面的代码中, 我们使用下面的命令生成了 Users 控制器和 new 动作$ rails generate controller Users new
创建模型有个类似的命令 —— generate model 。我们可以使用这个命令生成 User 模型, 以及 name 和 email 属性, 如下代码所示。
(1).生成 User 模型
注:控制器名是复数, 模型名是单数: 控制器是 Users , 而模型是 User 。我们指定了可选的参数 name:string 和 email:string , 告诉 Rails 我们需要的两个属性是什么, 以及各自的类型(两个都是字符串)。
执行上述 generate 命令之后, 会生成一个迁移文件。迁移是一种递进修改数据库结构的方式, 可以根据需求修改数据模型。执行上述 generate 命令后会自动为 User 模型创建迁移, 这个迁移的作用是创建一个 users 表, 以及 name 和 email 两个列, 如下代码所示:
(2).User 模型的迁移文件(创建 users 表)
打开文件:db/migrate/[timestamp]_create_users.rb
注:迁移文件名前面有个时间戳(timestamp),指明创建的时间。早期, 迁移文件名的前缀是递增的数字, 在团队协作中, 如果多人生成了序号相同的迁移文件就可能会发生冲突。除非两个迁移文件在同一秒钟生成这种小概率事件发生了, 否则使用时间戳基本可以避免冲突。
迁移文件中有一个名为 change 的方法,定义要对数据库做什么操作。在上图代码中, change 方法使用 Rails 提供的 create_table 方法在数据库中新建一个表,用于存储用户。create_table 方法可以接受一个块,有一个块变量 t (“table”)。在块中, create_table 方法通过 t 对象在数据库中创建 name 和 email 两个列,二者均为 string 类型。表名是复数形式( users ),不过模型名是单数形式( User ),这是 Rails 在用词上的一个约定:模型表示单个用户,而数据库表中存储了很多用户。块中最后一行 t.timestamps 是个特殊的方法,它会自动创建 created_at 和 updated_at 两个列,分别记录创建用户的时间戳和更新用户的时间戳。(前面有使用这两个列的例子。)这个迁移文件表示的完整数据模型如下图所示。
我们可以使用如下的 db:migrate 命令执行这个迁移(这叫“向上迁移”):
$ rails db:migrate
注:大多数迁移, 都是可逆的, 也就是说可以使用一个简单的命令“向下迁移”, 撤销之前的操作。这个命令是 db:rollback :
$ rails db:rollback
2.模型文件
(1).刚创建的 User 模型
打开文件:app/models/user.rb
注:前面说过, class User < ApplicationRecord 的意思是 User 类继承自 ApplicationRecord 类(而它继承自 ActiveRecord::Base 类), 所以 User 模型自动获得了 ActiveRecord::Base 的所有功能。
3. 创建用户对象
探索数据模型使用的工具是 Rails 控制台。因为我们(还)不想修改数据库中的数据, 所以要在沙盒(sandbox)模式中启动控制台:
$ rails console --sandbox
注:如提示消息所说, “Any modifications you make will be rolled back on exit”, 在沙盒模式下使用控制台, 退出当前会话后, 对数据库做的所有改动都会回归到原来的状态(即撤销)。
在前面的控制台会话中, 我们要引入必要的代码才能使用 User.new 创建用户对象。对模型来说, 情况有所不同。你可能还记得前面说过, Rails 控制台会自动加载 Rails 环境, 这其中就包括模型。也就是说, 现在无需加载任何代码就可以直接创建用户对象:
注:上述代码显示了用户对象在控制台中的默认表述
如果不为 User.new 指定参数, 对象的所有属性值都是 nil 。前面, 我们自己编写的 User 类可以接受一个散列参数, 指定用于初始化对象的属性。这种方式是受 Active Record 启发的, 在 Active Record 中也可以使用相同的方式指定初始值:
注:我们看到 name 和 email 属性的值都已经按预期设定了。
数据的有效性(validity)对理解 Active Record 模型对象很重要, 我们会在以后深入探讨。不过注意, 现在这个 user 对象是有效的, 我们可以在这个对象上调用 valid? 方法确认:
目前为止, 我们都没有修改数据库: User.new 只在内存中创建一个对象, user.valid? 只是检查对象是否有效。如果想把用户对象保存到数据库中, 要在 user 变量上调用 save 方法:
注:如果保存成功, save 方法返回 true , 否则返回 false 。(现在所有保存操作都会成功, 因为还没有数据验证; 等到了下面的内容就会看到一些失败的例子。) Rails 还会在控制台中显示 user.save 对应的 SQL 语句( INSERT INTO "users"... ), 以供参考。我们几乎不会使用原始的 SQL, 所以此后我会省略 SQL。不过, 从 Active Record各种操作生成的 SQL 中可以学到很多东西。
与以前定义的 User 类一样, User 模型的实例也可以使用点号获取属性:
等以后会介绍, 虽然一般习惯把创建和保存分成如上所示的两步完成, 不过 Active Record 也允许我们使用User.create 方法把这两步合成一步:
注:User.create 的返回值不是 true 或 false , 而是创建的用户对象, 可以直接赋值给变量(例如上面第二个命令中的 foo 变量)
create 的逆操作是 destroy :
奇怪的是, destroy 和 create 一样, 返回值是对象。我不觉得什么地方会用到 destroy 的返回值。更奇怪的是, 销毁的对象还在内存中:
注:那么我们怎么知道对象是否真被销毁了呢? 对于已经保存而没有销毁的对象, 怎样从数据库中读取呢? 要回答这些问题, 我们要先学习如何使用 Active Record 查找用户对象。
4.查找用户对象
Active Record 提供了好几种查找对象的方法。下面我们使用这些方法查找前面创建的第一个用户, 同时也验证一下第三个用户( foo )是否被销毁了。先看一下还存在的用户:
注:我们把用户的 ID 传给 User.find 方法, Active Record 会返回 ID 为 1 的用户对象。
下面来看一下 ID 为 3 的用户是否还在数据库中:
注:因为我们在前面销毁了第三个用户, 所以 Active Record 无法在数据库中找到这个用户, 从而抛出一个异常(exception), 这说明在查找过程中出现了问题。因为 ID 不存在, 所以 find 方法抛出 ActiveRe-cord::RecordNotFound 异常。
除了这种查找方式之外, Active Record 还支持通过属性查找用户(find_by):
注:我们将使用电子邮件地址做用户名, 在学习如何让用户登录网站时会用到这种 find 方法(后面的内容)。你可能会担心如果用户数量过多, 使用 find_by 的效率不高。事实的确如此, 我们会在下面说明这个问题, 以及如何使用数据库索引解决。
再介绍几个常用的查找方法。首先是 first 方法:
很明显, first 会返回数据库中的第一个用户。还有 all 方法:
从控制台的输出可以看出, User.all 方法返回一个 ActiveRecord::Relation 实例, 其实这是一个数组, 包含数据库中的所有用户。
5.更新用户对象
创建对象后, 一般都会进行更新操作。更新有两种基本方式, 其一, 可以分别为各个属性赋值, 在前面的内容中就是这么做的:
注:如果想把改动写入数据库, 必须执行最后一个方法。
我们可以执行 reload 命令来看一下没保存的话是什么情况。 reload 方法会使用数据库中的数据重新加载对象:
现在我们已经更新了用户数据,如前面所说,现在自动创建的那两个时间戳属性不一样了:
更新数据的第二种常用方式是使用 update_attributes 方法:
update_attributes 方法接受一个指定对象属性的散列作为参数, 如果操作成功, 会执行更新和保存两个操作(保存成功时返回 true )。
注:如果任何一个数据验证失败了, 例如存储记录时需要密码, update_attributes 操作就会失败。
如果只需要更新单个属性, 可以使用 update_attribute 方法, 跳过验证:
二. 验证用户数据
创建的 User 模型现在已经有了可以使用的 name 和 email 属性, 不过功能还很简单:任何字符串(包括空字符串)都可以使用。名字和电子邮件地址的格式显然要复杂一些。例如, name 不应该是空的, email 应该符合特定的格式。而且, 我们将把电子邮件地址当成用户名用来登录, 那么在数据库中就不能重复。
1.有效性测试
前面说过, TDD 并不适用所有情况, 但是模型验证是使用 TDD 的绝佳时机。如果不先编写失败测试, 再想办法让它通过, 我们很难确定验证是否实现了我们希望实现的功能。
我们采用的方法是, 先得到一个有效的模型对象, 然后把属性改为无效值, 以此确认这个对象是无效的。以防万一, 我们先编写一个测试, 确认模型对象一开始是有效的。这样, 如果验证测试失败了, 我们才知道的确事出有因(而不是因为一开始对象是无效的)。
前面代码中的命令生成了一个用于测试 User 模型的测试文件, 现在这个文件中还没什么内容, 如下图所示:
(1).还没什么内容的 User 模型测试文件
打开文件:test/models/user_test.rb
为了测试有效的对象,我们要在特殊的 setup 方法中创建一个有效的用户对象 @user 。前面有提到过, setup 方法会在每个测试方法运行前执行。因为 @user 是实例变量, 所以自动可在所有测试方法中使用, 而且我们可以使用 valid? 方法检查它是否有效。
测试如下图所示:
(2).测试用户对象一开始是有效的 GREEN
打开文件:test/models/user_test.rb
上图中使用简单的 assert 方法, 如果 @user.valid? 返回 true , 测试就能通过:返回 false , 测试则会失败。
因为 User 模型现在还没有任何验证,所有这个测试可以通过:
注:这里, 我们使用 rails test:models 命令, 只运行模型测试(与前面的 rails test:integration 对比一下)。
2.存在性验证
存在性验证算是最基本的验证了, 只是检查指定的属性是否存在。现在我们会确保用户存入数据库之前, name 和 email 字段都有值。以后会介绍如何把这个限制应用到创建用户的注册表单中。
我们要先在前面写好的测试文件的基础上再编写一个测试, 检查 name 属性是否存在。如下图所示, 我们只需把 @user 变量的 name 属性设为空字符串(包含几个空格的字符串), 然后使用 assert_not 方法确认得到的用户对象是无效的。
(1).测试 name 属性的验证 RED
打开文件:test/models/user_test.rb
现在,模型测试应该失败:
我们在前面应该见过, name 属性的存在性验证使用 validates 方法,而且其参数为 presence: true , 如下图所示。 presence: true 是只有一个元素的可选散列参数; 前面说过, 如果方法的最后一个参数是散列, 可以省略花括号。(前面曾说过,Rails 经常使用散列做参数。)
(2).为 name 属性添加存在性验证 GREEN
打开文件:app/models/user.rb
上图中的代码看起来可能有点儿神奇, 其实 validates 就是个方法。加入括号后, 可以写成:
打开控制台, 看一下在 User 模型中加入验证后有什么效果:
这里我们使用 valid? 方法检查 user 变量的有效性, 如果有一个或多个验证失败, 返回值为 false ; 如果所有验证都能通过, 返回 true 。现在只有一个验证, 所以我们知道是哪一个失败, 不过看一下失败时生成的 er-rors 对象还是很有用的:
因为用户无效, 如果尝试把它保存到数据库中, 操作会失败:
加入验证后, 测试应该可以通过了:
(3).测试 email 属性的验证 RED
打开文件:test/models/user_test.rb
(4).为 email 属性添加存在性验证 GREEN
打开文件:app/models/user.rb
现在,存在性验证都添加了,测试组件应该可以通过了:
3.长度验证
我们已经对 User 模型可接受的数据做了一些限制, 现在必须为用户提供一个名字, 不过我们应该做进一步限制, 因为用户的名字会在演示应用中显示, 所以最好限制它的长度(name:50为上限, email:244为上限)
(1).测试 name 属性的长度验证 RED
打开文件:test/models/user_test.rb
现在测试, 应该是失败的:
为了让测试通过, 我们要使用验证参数限制长度, 即 length , 以及限制上线的 maximum 参数, 如下图所示:
(2).为 name 属性添加长度验证 GREEN
现在测试应该可以通过了:
4.格式验证
电子邮件地址格式验证有点棘手, 而且容易出错, 所以我们会先编写检查有效电子邮件地址的测试, 这些测试应该能通过, 以此捕获验证可能出现的错误。也就是说, 添加验证后, 不仅要拒绝无效的电子邮件地址, 例如 user@163,com,还得接受有效的电子邮件地址, 例如 user@163.com。(显然目前会接受所有电子邮件地址, 因为只要不为空值都能通过验证。)检查有效电子邮件地址的测试如下图所示:
(1).测试有效的电子邮件地址格式 GREEN
打开文件:test/models/user_test.rb
注意,我们为 assert 方法指定了可选的第二个参数, 用于定制错误消息, 识别是哪个地址导致测试失败的:
assert @user.valid?, "#{valid_address.inspect} should be valid"
这行代码在字符串插值中使用了前面介绍的 inspect 方法。像这种使用 each 方法的测试, 最好能知道是哪个地址导致失败的, 因为不管哪个地址导致测试失败, 都无法看到行号, 很难查出问题的根源。
接下来, 我们要测试几个无效的电子邮件, 确认它们无法通过验证, 例如 user@example,com(点号变成了逗号)和 user_at_foo.qq.com(没有“@”符号)。与上图一样, 下图也指定了错误消息参数, 识别是哪个地址导致测试失败的。
(2).测试电子邮件地址格式验证 RED
打开文件:test/models/user_test.rb
现在, 测试应该失败:
电子邮件地址格式验证使用 format 参数, 用法如下:
validates :email, format: { with: /<regular expression>/ }
它使用指定的正则表达式(regular expression, 简称 regex)验证属性。正则表达式很强大, 使用模式匹配字符串, 但往往晦涩难懂。我们要编写一个正则表达式, 匹配有效的电子邮件地址, 但不匹配无效的地址。在官方标准中其实有一个正则表达式, 可以匹配全部有效的电子邮件地址, 但没必要使用这么复杂的正则表达式。我们使用一个更务实的正则表达式, 能很好地满足实际需求, 如下所示:
VALID_EMAIL_REGEX = /\A[\w+\-.]+@[a-z\d\-.]+\.[a-z]+\z/i
(3).使用正则表达式验证电子邮件地址的格式 GREEN
打开文件:app/models/user.rb
其中, VALID_EMAIL_REGEX 是一个常量(constant)。在 Ruby 中常量的首字母为大写。下面这段代码:
VALID_EMAIL_REGEX = /\A[\w+\-.]+@[a-z\d\-.]+\.[a-z]+\z/i
validates :email, presence: true, length: {maximum: 255}, format: {with: VALID_EMAIL_REGEX}
注:确保只有匹配正则表达式的电子邮件地址才是有效的。这个正则表达式有一个缺陷:能匹配 foo@qq..com 这种有连续点号的地址。修正这个瑕疵需要一个更复杂的正则表达式, 这里我就懒得弄了, 你到网上粘一个下来就可以了
现在测试应该可以通过了:
那么, 现在就只剩一个限制要实现了:确保电子邮件地址的唯一性。
5.唯一性验证
我们要先编写一些简短的测试。之前的模型测试, 只是使用 User.new 在内存中创建一个 Ruby 对象, 但是测试唯一性时要把数据存入数据库。对重复电子邮件地址的测试如下图所示:
(1).拒绝重复电子邮件地址的测试 RED
打开文件:test/models/user_test.rb
我们使用 @user.dup 方法创建一个和 @user 的电子邮件地址一样的用户对象,然后保存 @user , 因为数据库中的 @user 已经占用了这个电子邮件地址, 所以 duplicate_user 对象无效。
在 email 属性的验证中加入 uniqueness: true 可以让上图中的测试通过, 如下图所示:
(2).电子邮件地址唯一性验证 GREEN
打开文件:app/models/user.rb
这还不行,一般来说电子邮件地址不区分大小写,也就说 foo@QQ.com 和 FOO@QQ.COM 或 FoO@Qq.coM 是同一个地址, 所以验证时也要考虑这种情况。
因此, 还要测试不区分大小写, 如下图所示:
(3).测试电子邮件地址的唯一性验证不区分大小写 RED
打开文件:test/models/user_test.rb
上面的代码, 在字符串上调用 upcase 方法。这个测试和前面对重复电子邮件的测试作用一样, 只是把地址转换成全部大写字母的形式。如果你觉得太抽象, 那就在控制台中实操一下吧:
当然, 现在 duplicate_user.valid? 的返回值是 true , 因为唯一性验证还区分大小写。我们希望得到的结果是 false 。幸好 :uniqueness 可以指定 :case_sensitive 选项, 正好可以解决这个问题, 如下图所示:
(3).电子邮件地址唯一性验证,不区分大小写 GREEN
打开文件:app/models/user.rb
注:我们直接把 true 换成了 case_sensitive: false , Rails 会自动指定 :uniqueness 的值为 true 。
至此, 我们的应用虽然还有不足, 但基本可以保证电子邮件地址的唯一性了, 测试组件应该可以通过了:
现在还有一个小问题——Active Record 中的唯一性验证无法保证数据库层也能实现唯一性。我来解释一下:
1. 我使用 wahaha@163.com 在演示应用中注册;
2. 不小心按了两次提交按钮,连续发送了两次请求;
3. 然后就会发生这种事情:请求 1 在内存中新建了一个用户对象,能通过验证;请求 2 也一样。请求 1 创建的用户存入了数据库,请求 2 创建的用户也存入了数据库。
4. 结果是,尽管有唯一性验证,数据库中还是有两条用户记录的电子邮件地址是一样的。
注:上面这种难以置信的情况可能发生, 只要有一定的访问量, 在任何 Rails 网站中都可能发生(这是我从教训中学到的经验)。幸好解决的方法很简单, 只需在数据库层也加上唯一性限制。我们要做的是在数据库中为 email 列建立索引,然后为索引加上唯一性约束。
在数据库中创建列时要考虑是否需要通过这个列查找记录。以前面代码中的迁移创建的 email 属性为例,等以后实现登录功能后,我们将根据提交的电子邮件地址查找对应的用户记录。可是,在这个简单的数据模型中通过电子邮件地址查找用户只有一种方法——检查数据库中的所有用户记录,比较记录中的 email 属性和指定的电子邮件地址。也就是说,可能要检查每一条记录(毕竟用户可能是数据库中的最后一条记录)。在数据库领域,这叫全表扫描(full-table scan)。如果网站中有几千个用户,这可不是一件轻松的事。
为 email 列加上索引可以解决这个问题。我们可以把数据库索引看成书籍的索引。如果要在一本书中找出某个字符串(例如 "foobar" )出现的所有位置,需要翻看书中的每一页。但是如果有索引的话,只需在索引中找到 "foobar" 条目,就能看到所有包含 "foobar" 的页码。数据库索引基本上也是这种原理。
数据库索引
为 email 列建立索引要改变数据模型, 在 Rails 中可以通过迁移实现。在前面我们知道了, 生成 User 模型时会自动创建一个迁移文件。现在我们是要改变已经存在的模型结构, 那么使用 migration 命令直接创建迁移文件就可以了:
$ rails generate migration add_index_to_users_email
与 User 模型的迁移不同, 实现电子邮件地址唯一性的操作没有事先定义好的模板可用, 所以我们要自己动手编写, 如下图所示:
(4).添加电子邮件唯一性约束的迁移
打开文件:db/migrate/[timestamp]_add_index_to_users_email.rb
注:上述代码调用了 Rails 中的 add_index 方法, 为 users 表中的 email 列建立索引。索引本身并不能保证唯一性, 所以还要指定 unique: true 。
最后, 执行数据库迁移:
$ rails db:migrate
注:如果迁移失败的话,退出所有打开的沙盒模式控制台会话试试。这些会话可能会锁定数据库,拒绝迁移操作。
现在测试组件应该无法通过, 因为固件(fixture)中的数据违背了唯一性约束。固件的作用是为测试数据库提供示例数据。执行创建数据库的命令时会自动生成用户固件, 如下图所示, 电子邮件地址有重复。(电子邮件地址也无效, 但固件中的数据不会应用验证规则。)
(5).默认生成的用户固件 RED
打开文件:test/fixtures/users.yml
我们现在还用不到固件, 现在暂且把其中的数据删除, 只留下一个空文件, 如代码清单 6.31 所示:
为了保证电子邮件地址的唯一性, 还要做些修改。有些数据库适配器的索引区分大小写, 会把“Foo@QQ.CoM”和“foo@qq.com”视作不同的字符串, 但我们的应用会把它们看做同一个地址。为了避免不兼容, 我们要统一使用小写形式的地址, 存入数据库前, 把“Foo@QQ.CoM”转换成“foo@qq.com”。为此, 我们要使用回调(callback), 在 Active Record 对象生命周期的特定时刻调用。这里, 我们要使用的回调是 before_save , 在用户对象存入数据库之前把电子邮件地址转换成全小写字母形式, 如下图所示。(这只是初步实现方式, 以会再次完善这个东西, 届时会使用常用的“方法引用”定义回调。)
(6).把 email 属性的值转换为小写形式,确保电子邮件地址的唯一性 GREEN
打开文件:app/models/user.rb
注:before_save 后有一个块,块中的代码调用字符串的 downcase 方法,把用户的电子邮件地址转换成小写形式。
在上图中,我们可以把赋值语句写成:
self.email = self.email.downcase
其中 self 表示当前用户。但是在 User 模型中,右侧的 self 关键字是可选的,我们在 palindrome? 方法中调用 reverse 方法时说过:
self.email = email.downcase
注意,左侧的 self 不能省略,所以写成
email = email.downcase
是不对的。
现在,前面遇到的问题解决了,数据库会存储请求 1 创建的用户,不会存储请求 2 创建的用户,因为后者违反了唯一性约束。(在 Rails 的日志中会显示一个错误,不过并无大碍。)为 email 列建立索引同时也解决了:为 email 列添加索引之后,使用电子邮件地址查找用户时不会进行全表扫描,从而解决了潜在的效率问题。
(7).上图中把电子邮件地址转换成小写形式的测试 GREEN
打开文件:test/models/user_test.rb
(8). before_save 回调的另一种实现方式 GREEN
。。。
累了,累了,歇会
Ruby Rails学习中:User 模型,验证用户数据的更多相关文章
- Ruby Rails学习中:登陆
登陆 一. Sessions 控制器 登录和退出功能由 Sessions 控制器中相应的 REST 动作处理 : 登录表单在 new 动作中处理, 登录的过程是向 create 动作发送 POST 请 ...
- Ruby Rails学习中:注册表单,注册失败,注册成功
接上篇 一. 注册表单 用户资料页面已经可以访问了, 但内容还不完整.下面我们要为网站创建一个注册表单. 1.使用 form_for 注册页面的核心是一个表单, 用于提交注册相关的信息(名字.电子邮件 ...
- Ruby Rails学习中:添加安全密码
接上篇 一. 添加安全密码 我们已经为 name 和 email 字段添加了验证规则, 现在要加入用户所需的最后一个常规属性: 安全密码.每个用户都要设置一个密码(还要二次确认), 数据库中则存储经过 ...
- Ruby Rails学习中:调试信息和 Rails 的三种环境,Users 资源,调试器,Gravatar 头像和侧边栏
注册 一.调试信息和 Rails 环境 现在咱们要实现的用户资料页面是我们这个应用中第一个真正意义上的动态页面.虽然视图的代码不会动态改变, 不过每个用户资料页面显示的内容却是从数据库中读取的.添加动 ...
- Ruby Rails学习中:Sass 和 Asset Pipeline,布局中的链接(Rails路由,具名路由),用户注册: 第一步
接上篇: 一.Sass 和 Asset Pipeline Rails 中最有用的功能之一是 Asset Pipeline, 它极大地简化了静态资源文件(CSS.JavaScript 和图像)的生成和管 ...
- Ruby Rails学习中:Ruby内置的辅助方法,基础内容回顾补充
一. Ruby内置的辅助方法 1.打开文件:app/views/layouts/application.html.erb(演示应用的网站布局) 来咱把注意力放在圈起来的那一行: 这行代码使用 Rail ...
- Ruby Rails学习中:有点内容的静态页面
续上篇: 一. 有点内容的静态页面 rails new 命令创建了一个布局文件, 不过现在最好不用.我们重命名这个文件: $ mv app/views/layouts/application.html ...
- Ruby Rails学习中:网站导航,Bootstrap和自定义的CSS,局部视图
添加一些结构 一.网站导航 1.添加一些结构后的网站布局文件 打开文件:app/views/layouts/application.html.erb 简单介绍一下,添加的代码: 我们从上往下看一下这段 ...
- Ruby Rails学习中:关于测试的补充,MiniTest报告程序,Guard自动测试
一. 关于测试的补充 1.MiniTest报告程序 为了让 Rails 应用的测试适时显示红色和绿色,我建议你在测试辅助文件中加入以下内容: (1).打开文件:test/test_helper.rb ...
随机推荐
- codeforces#1165 F2. Microtransactions (hard version) (二分+贪心)
题目链接: https://codeforces.com/contest/1165/problem/F2 题意: 需要买$n$种物品,每种物品$k_i$个,每个物品需要两个硬币 每天获得一个硬币 有$ ...
- JAVA编程思想第二章答案
欢迎访问我的CSDN博客查看https://mp.csdn.net/mdeditor/94797839# 有其他问题欢迎发送邮箱至hpzhangjunjiell@163.com 感谢
- 【redis 学习系列】API的理解与使用(四)
5.集合 集合(set)类型也是用来保存多个字符串元素,但是与列表不一样的是,集合中不允许有重复的元素,并且集合中的元素是无序的,不能通过索引下标获取元素. 如图2-22所示,集合user:1:fol ...
- AngularJS复习小结
开发移动端App,首先得在头部 <meta name="viewport" content="width=device-width, initial-scale=1 ...
- macos npm + node 环境启动问题排查
MacOS安装npm全局包的权限问题 解决办法:修改npm包所安装目录的权限:sudo chown -R $USER /usr/local 然后输入密码就可以了 deMBP:~ $ sudo ch ...
- echart itemStyle属性设置
itemStyle // itemStyle 设置饼状图扇形区域样式 itemStyle: { // emphasis:英文意思是 强调;着重; ...
- Rocketmq同步发送消息
package com.bfxy.rocketmq.quickstart; import org.apache.rocketmq.client.exception.MQBrokerException; ...
- Android跨进程通信广播(Broadcast)
广播是一种被动跨进程通讯的方式.当某个程序向系统发送广播时,其他的应用程序只能被动地接收广播数据.这就象电台进行广播一样,听众只能被动地收听,而不能主动与电台进行沟通,在应用程序中发送广播比较简单.只 ...
- linux内核中的subsys_initcall是干什么的?
注意:使用的内核源码版本为5.1.3 1. subsys_initcall长什么样子? 它其实是个宏定义,定义如下: #define subsys_initcall(fn) __define_ ...
- [Java读书笔记] Effective Java(Third Edition) 第2章 创建和销毁对象
第 1 条:用静态工厂方法代替构造器 对于类而言,获取一个实例的方法,传统是提供一个共有的构造器. 类可以提供一个公有静态工厂方法(static factory method), 它只是一个返回类 ...