9个重要的Ruby on Rails面试问题 *

最优秀的Ruby on Rails开发人员和工程师可以回答的基本问题. 在我们社区的推动下,我们鼓励专家提交问题并提供反馈.

现在就雇佣一名优秀的Ruby on Rails开发人员
Toptal logo是顶级自由软件开发人员的专属网络吗, designers, finance experts, product managers, and project managers in the world. 顶级公司雇佣Toptal自由职业者来完成他们最重要的项目.

Interview Questions

1.

What is CSRF? How does Rails protect against it?

View answer

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令牌.

2.

鲁比的有什么不同 Hash and ActiveSupport’s HashWithIndifferentAccess?

View answer

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"
3.

请注意:这个面试问题只适用于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
View answer

将用户提供的参数转换为符号是危险的,因为 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开发人员工作

Apply as a Freelancer
4.

下面的控制器代码有什么问题? 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
View answer

这是臭名昭著的“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 记录从数据库发出后立即进行初始请求 PostS,从而将数据库请求的数量减少到只有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代码)转换为单个数据库查询.

5.

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"]
View answer

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 需要对数据库中的表进行更改.

6.

下面的代码片段将定义哪些路径(HTTP动词和URL) config/routes.rb?

resources :posts do
  member do
    get 'comments'
  end
  collection do
    post 'bulk_upload'
  end
end
View answer

Using the resource 方法定义路由时,将自动为 standard seven restful actions:

  1. GET /posts
  2. POST /posts
  3. GET /posts/new
  4. GET /posts/:id/edit
  5. GET /posts/:id
  6. PATCH/PUT /posts/:id
  7. 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).

7.

创建一个路由,以便能够显示包含不同类型啤酒的不同信息的页面. 路由应该能识别如下URL路径 /beer/ 并且应该对每种类型的啤酒使用相同的控制器动作,并将实际的啤酒类型作为参数传递给控制器动作. The valid beer types are:

  • IPA
  • brown_ale
  • pilsner
  • lager
  • lambic
  • hefeweizen

指定的任何其他类型的啤酒都应该生成404状态码.

View answer

一种方法是生成一个简单的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('|'))}
8.

假设我们有一个id= " 4 "的Student. 如果我们删除id= " 4 "的Student,下面的查询结果会是什么?

  • Student.find(4)
  • Student.find_by_id(4)
View answer

Solution

  • Student.find(4) will raise an error: ActiveRecord::RecordNotFound:找不到id=4的学生
  • Student.find_by_id(4) will return nil and will not raise an error.
9.

Given this input:

x = [{"a" => 10},{"b" => 20},{"c" => 30}]

How can you obtain the following?

  1. one array containing all keys
  2. another containing all values
  3. the sum of all the values
View answer

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 Developer

Job Opportunities From Our Network

Submit an interview question

提交的问题和答案将被审查和编辑, 并可能会或可能不会选择张贴, at the sole discretion of Toptal, LLC.

* All fields are required

寻找Ruby on Rails开发人员?

Looking for Ruby on Rails Developers? 查看Toptal的Ruby on Rails开发人员.

Bruz Marzolf

Freelance Ruby on Rails Developer

United StatesToptal Member Since January 14, 2016

Bruz是一名全栈开发人员,自从15年前开始做这个工作以来,他就喜欢为网络构建东西. Bruz花了很多时间在后端使用Ruby和Elixir构建服务,处理数据库和基础设施,但在前端使用JavaScript构建应用程序方面也做了大量工作.

Show More

Eqbal Quran

Freelance Ruby on Rails Developer

JordanToptal Member Since June 13, 2014

Eqbal是一名高级全栈开发人员,拥有超过十年的web和移动开发经验. 他是解决问题的高手,并拥有广泛的成品专业产品组合.

Show More

Tina Holly

Freelance Ruby on Rails Developer

CanadaToptal Member Since March 9, 2016

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 More

Toptal Connects the Top 3% 世界各地的自由职业人才.

Join the Toptal community.

Learn more