9个重要的Ruby on Rails面试问题 *
最优秀的Ruby on Rails开发人员和工程师可以回答的基本问题. 在我们社区的推动下,我们鼓励专家提交问题并提供反馈.
现在就雇佣一名优秀的Ruby on Rails开发人员Interview Questions
CSRF stands for Cross-Site Request Forgery. 这是一种攻击形式,攻击者以您的名义向其他网站提交表单, 可能造成损害或泄露敏感信息. 因为浏览器会自动在请求中包含一个域的cookie, 如果您最近登录过目标站点, 攻击者的请求看起来像是来自已登录用户的您(因为您的会话cookie将与 POST
request).
为了防止CSRF攻击,您可以添加 protect_from_forgery
to your ApplicationController
. 这将导致Rails在接受CSRF令牌之前要求提供CSRF令牌 POST
, PUT
, or DELETE
requests. CSRF令牌作为隐藏字段包含在使用Rails表单构建器创建的每个表单中. It is also included as a header in GET
请求,以便其他非基于表单的机制发送 POST
can use it as well. 浏览器的“同源”策略阻止攻击者窃取CSRF令牌.
The Hash
类通过执行一个标准来检索值 ==
comparison on the keys. 这意味着为 Symbol
key (e.g. :my_value)
不能使用等价的检索 String
(e.g. ‘my_value’). On the other hand,
HashWithIndifferentAccess
treats Symbol
keys and String
keys as equivalent so that
the following would work:
h = HashWithIndifferentAccess.new
h[:my_value] = 'foo'
h['my_value'] #=> will return "foo"
请注意:这个面试问题只适用于ruby -2之前的遗产.2 projects.
下面的控制器代码有什么问题? What would be the consequence of 将这段代码留在生产应用程序中? How would you fix it?
class MyController < ApplicationController
def options
options = {}
Available_option_keys = [:first_option,:second_option,:third_option]
all_keys = params.keys.map(&:to_sym)
set_option_keys = all_keys & available_option_keys
set_option_keys.each do |key|
options[key] = params[key]
end
options
end
end
将用户提供的参数转换为符号是危险的,因为 Symbol
Ruby中的对象不会被垃圾收集. 攻击者可以发送一系列带有随机密钥的请求,这些密钥将被转换成符号, 很快耗尽服务器的可用内存并关闭站点.
有两种方法可以解决这个问题. The first would be to use slice
to eliminate values from the params
hash that are not valid option keys. This would look something like:
params.slice(*available_option_keys)
另一种选择,有些人认为是更好的选择,就是使用 String
keys for your options. 除非你有非常多的选项键, 您实际上不会通过使用节省那么多内存 Symbol
keys instead.
申请加入Toptal的发展网络
and enjoy reliable, steady, remote 自由Ruby on Rails开发人员工作
下面的控制器代码有什么问题? How would you fix it?
class CommentsController < ApplicationController
def users_comments
posts = Post.all
comments = posts.map(&:comments).flatten
@user_comments = comments.select do |comment|
comment.author.username == params[:username]
end
end
end
这是臭名昭著的“n+1”漏洞的典型例子. 第一行将检索所有的 Post
对象,但是下一行将为每个对象发出额外的请求 Post
to retrieve the corresponding Comment
objects. 更糟糕的是,这段代码正在制造 even more 数据库请求,以便检索 Author
of each Comment
.
这可以通过将方法中的第一行更改为:
posts = Post.includes(comments: [:author]).all
这告诉ActiveRecord检索相应的 Comment
and Author
记录从数据库发出后立即进行初始请求 Post
S,从而将数据库请求的数量减少到只有3个.
请注意,上述答案只是可能避免招致“n+1”惩罚的几种方法之一, 每种选择都有自己的警告和极端情况. 选择上面的答案在这里展示,因为它只需要对现有代码进行最小的更改,并且没有对的反向关联进行假设 Comment
to Post
.
Incidentally, there’s another issue here (although not what we’re focused on in this question and answer); namely, 在Ruby中执行查询,而不是在数据库中执行查询(在数据库中可能会更快)!). 这样一个相对复杂的查询可以很容易地在ActiveRecord中构造, 从而将3个数据库查询操作(加上一些执行的Ruby代码)转换为单个数据库查询.
How would you define a Person
model so that any Person
can be assigned as the parent
of another Person
(如下面的Rails控制台所示)? 在为其创建表的迁移中需要定义哪些列 Person
?
irb(main):001:0> john = Person.create(name: "John")
irb(main):002:0> jim = Person.create(name: "Jim", parent: john)
irb(main):003:0> bob = Person.create(name: "Bob", parent: john)
irb(main):004:0> john.children.map(&:name)
=> ["Jim", "Bob"]
对于更高级的挑战:更新 Person
模型,以便您还可以获得一个人的所有孙子孙女的列表,如下所示. 您是否需要对数据库中的相应表进行任何更改?
irb(main):001:0> sally = Person.create(name: "Sally")
irb(main):002:0> sue = Person.create(name: "Sue", parent: sally)
irb(main):003:0> kate = Person.创建(名字:“Kate”,家长:sally)
irb(main):004:0> lisa = Person.create(name: "Lisa", parent: sue)
irb(main):005:0> robin = Person.create(name: "Robin", parent: kate)
irb(main):006:0> donna = Person.create(name: "Donna", parent: kate)
irb(main):007:0> sally.grandchildren.map(&:name)
=> ["Lisa", "Robin", "Donna"]
Normally, the target class of an ActiveRecord
关联是从关联的名称推断出来的(“约定优于配置”的完美示例). 但是,可以覆盖此默认行为,并指定不同的目标类. 这样做,甚至可以在同一类的两个对象之间建立关系.
这就是建立亲子关系的可能方式. 模型定义如下:
class Person < ActiveRecord::Base
belongs_to :parent, class: Person
has_many:children, class: Person, foreign_key: parent_id
end
It’s necessary to specify the foreign_key
option for the has_many
relationship because ActiveRecord
will attempt to use :person_id
by default. 在迁移中为这个模型创建表, you would need to define, at minimum, a column for the name
属性以及用于的整数列 parent_id
.
自引用关系可以以与正常双模型关系相同的方式进行扩展. This even includes has_many ... :through => ...
style relationships. 但是,因为我们要绕过Rails的约定,所以我们需要指定 :through
in the case of adding a grandchild
relationship:
class Person < ActiveRecord::Base
belongs_to :parent, class: Person
has_many:children, class: Person, foreign_key: parent_id
has_many:孙子,class: Person, through: children, source::children
end
因此,由于这仍然只是使用 parent_id
defined in the first case, no
需要对数据库中的表进行更改.
下面的代码片段将定义哪些路径(HTTP动词和URL) config/routes.rb
?
resources :posts do
member do
get 'comments'
end
collection do
post 'bulk_upload'
end
end
Using the resource
方法定义路由时,将自动为
standard seven restful actions:
GET /posts
POST /posts
GET /posts/new
GET /posts/:id/edit
GET /posts/:id
PATCH/PUT /posts/:id
DELETE /posts/:id
注意,Rails还支持(相对较新的)URL谓词 PATCH
for partial updates to records. (In theory, a PUT
只有在请求中包含整个记录时,请求才有效.)
在块内部定义的额外路由传递给 resources
will generate one route
valid for individual posts (GET /posts/:id/comments
)以及为顶级资源(POST /posts/bulk_upload
).
创建一个路由,以便能够显示包含不同类型啤酒的不同信息的页面. 路由应该能识别如下URL路径 /beer/
并且应该对每种类型的啤酒使用相同的控制器动作,并将实际的啤酒类型作为参数传递给控制器动作. The valid beer types are:
- IPA
- brown_ale
- pilsner
- lager
- lambic
- hefeweizen
指定的任何其他类型的啤酒都应该生成404状态码.
一种方法是生成一个简单的get路由,指定要调用的控制器动作,并将啤酒的种类作为参数传递:
get 'beers/:kind' => 'beers#kind'
然后,在控制器动作的上下文中,如果 kind
参数未包含在有效类型列表中,则该操作会引发 ActionController::RoutingError
,在生产环境中将重定向到404.
Alternatively, a simpler solution 是根据路由定义中的有效类型列表进行检查. This can be accomplished using the constraints
option as follows:
种类= %w|IPA棕色啤酒比尔森啤酒兰姆比克啤酒
get 'beers/:kind' => 'beers#kind', constraints: {kind: Regexp.new(kinds.join('|'))}
This code calls the BeersController#kind
action method with params['kind']
设置为表示URL路径中给定的啤酒类型的字符串. The key is using the constraints
选项,用于指定用于验证路由是否正确的正则表达式. 在本例中,lambda检查 kind
参数包含在有效啤酒类型列表中.
Or perhaps an even better solution would be to use resource routing. 这样做的额外好处是提供了URL生成帮助程序, 但代价是需要将啤酒的参数名传递为 :id
. This would look something like:
种类= %w|IPA棕色啤酒比尔森啤酒兰姆比克啤酒
资源:beer, only: [:show],约束:{id: Regexp.new(kinds.join('|'))}
假设我们有一个id= " 4 "的Student. 如果我们删除id= " 4 "的Student,下面的查询结果会是什么?
Student.find(4)
Student.find_by_id(4)
Solution
-
Student.find(4)
will raise an error:ActiveRecord::RecordNotFound:找不到id=4的学生
-
Student.find_by_id(4)
will returnnil
and will not raise an error.
Given this input:
x = [{"a" => 10},{"b" => 20},{"c" => 30}]
How can you obtain the following?
- one array containing all keys
- another containing all values
- the sum of all the values
This works:
y = x[0].merge(x[1]).merge(x[2])
y.Keys #将返回所有的键
y.Values #将返回所有值
y.values.Inject(:+) #将返回所有值的总和
但更好的开头应该是这样的:
y = x.reduce(:merge)
因为它可以处理任何大小的数组,而不仅仅是给定的精确输入.
面试不仅仅是棘手的技术问题, 所以这些只是作为一个指南. 并不是每一个值得雇佣的“A”候选人都能回答所有的问题, 回答所有问题也不能保证成为A级考生. At the end of the day, 招聘仍然是一门艺术,一门科学,需要大量的工作.
Why Toptal
Tired of interviewing candidates? 不知道该问什么才能让你得到一份好工作?
让Toptal为你找到最合适的人.
现在就雇佣一名优秀的Ruby on Rails开发人员我们的独家Ruby on Rails开发人员网络
希望找到一份Ruby on Rails开发人员的工作?
让Toptal为你找到合适的工作.
Apply as a Ruby on Rails DeveloperJob Opportunities From Our Network
Submit an interview question
提交的问题和答案将被审查和编辑, 并可能会或可能不会选择张贴, at the sole discretion of Toptal, LLC.
寻找Ruby on Rails开发人员?
Looking for Ruby on Rails Developers? 查看Toptal的Ruby on Rails开发人员.
Bruz Marzolf
Freelance Ruby on Rails Developer
Bruz是一名全栈开发人员,自从15年前开始做这个工作以来,他就喜欢为网络构建东西. Bruz花了很多时间在后端使用Ruby和Elixir构建服务,处理数据库和基础设施,但在前端使用JavaScript构建应用程序方面也做了大量工作.
Show MoreEqbal Quran
Freelance Ruby on Rails Developer
Eqbal是一名高级全栈开发人员,拥有超过十年的web和移动开发经验. 他是解决问题的高手,并拥有广泛的成品专业产品组合.
Show MoreTina Holly
Freelance Ruby on Rails Developer
Tina是一位经验丰富的全栈工程师,专门从事移动优先响应式web开发和客户端JavaScript应用程序(使用React), Redux, Angular, Vue, ember和他们的api——ruby on Rails, Node.js, Kotlin和java -由SQL和no-SQL数据库支持. Tina还擅长为iOS和Android开发原生移动应用, including KMM, 并且拥有React Native和Cordova的专业知识.
Show MoreToptal Connects the Top 3% 世界各地的自由职业人才.
Join the Toptal community.