class RuboCop::Cop::Lint::NonDeterministicRequireOrder

Dir` and `Dir.glob(…)` do not make any guarantees about the order in which files are returned. The final order is determined by the operating system and file system. This means that using them in cases where the order matters, such as requiring files, can lead to intermittent failures that are hard to debug. To ensure this doesn’t happen, always sort the list.

‘Dir.glob` and `Dir[]` sort globbed results by default in Ruby 3.0. So all bad cases are acceptable when Ruby 3.0 or higher are used.

NOTE: This cop will be deprecated and removed when supporting only Ruby 3.0 and higher.

@safety

This cop is unsafe in the case where sorting files changes existing
expected behavior.

@example

# bad
Dir["./lib/**/*.rb"].each do |file|
  require file
end

# good
Dir["./lib/**/*.rb"].sort.each do |file|
  require file
end

# bad
Dir.glob(Rails.root.join(__dir__, 'test', '*.rb')) do |file|
  require file
end

# good
Dir.glob(Rails.root.join(__dir__, 'test', '*.rb')).sort.each do |file|
  require file
end

# bad
Dir['./lib/**/*.rb'].each(&method(:require))

# good
Dir['./lib/**/*.rb'].sort.each(&method(:require))

# bad
Dir.glob(Rails.root.join('test', '*.rb'), &method(:require))

# good
Dir.glob(Rails.root.join('test', '*.rb')).sort.each(&method(:require))

# good - Respect intent if `sort` keyword option is specified in Ruby 3.0 or higher.
Dir.glob(Rails.root.join(__dir__, 'test', '*.rb'), sort: false).each(&method(:require))

Constants

MSG

Public Instance Methods

on_block(node) click to toggle source
# File lib/rubocop/cop/lint/non_deterministic_require_order.rb, line 65
def on_block(node)
  return if target_ruby_version >= 3.0
  return unless node.body
  return unless unsorted_dir_loop?(node.send_node)

  loop_variable(node.arguments) do |var_name|
    return unless var_is_required?(node.body, var_name)

    add_offense(node.send_node) { |corrector| correct_block(corrector, node.send_node) }
  end
end
on_block_pass(node) click to toggle source
# File lib/rubocop/cop/lint/non_deterministic_require_order.rb, line 89
def on_block_pass(node)
  return if target_ruby_version >= 3.0
  return unless method_require?(node)
  return unless unsorted_dir_pass?(node.parent)

  parent_node = node.parent

  add_offense(parent_node) do |corrector|
    if parent_node.arguments.last&.block_pass_type?
      correct_block_pass(corrector, parent_node)
    else
      correct_block(corrector, parent_node)
    end
  end
end
on_numblock(node) click to toggle source
# File lib/rubocop/cop/lint/non_deterministic_require_order.rb, line 77
def on_numblock(node)
  return if target_ruby_version >= 3.0
  return unless node.body
  return unless unsorted_dir_loop?(node.send_node)

  node.argument_list
      .filter { |argument| var_is_required?(node.body, argument.name) }
      .each do
        add_offense(node.send_node) { |corrector| correct_block(corrector, node.send_node) }
      end
end

Private Instance Methods

correct_block(corrector, node) click to toggle source
# File lib/rubocop/cop/lint/non_deterministic_require_order.rb, line 107
def correct_block(corrector, node)
  if unsorted_dir_block?(node)
    corrector.replace(node, "#{node.source}.sort.each")
  else
    source = node.receiver.source

    corrector.replace(node, "#{source}.sort.each")
  end
end
correct_block_pass(corrector, node) click to toggle source
# File lib/rubocop/cop/lint/non_deterministic_require_order.rb, line 117
def correct_block_pass(corrector, node)
  if unsorted_dir_glob_pass?(node)
    block_arg = node.arguments.last

    corrector.remove(last_arg_range(node))
    corrector.insert_after(node, ".sort.each(#{block_arg.source})")
  else
    corrector.replace(node.loc.selector, 'sort.each')
  end
end
last_arg_range(node) click to toggle source

Returns range of last argument including comma and whitespace.

@return [Parser::Source::Range]

# File lib/rubocop/cop/lint/non_deterministic_require_order.rb, line 132
def last_arg_range(node)
  node.arguments.last.source_range.with(
    begin_pos: node.arguments[-2].source_range.end_pos
  )
end
unsorted_dir_loop?(node) click to toggle source
# File lib/rubocop/cop/lint/non_deterministic_require_order.rb, line 138
def unsorted_dir_loop?(node)
  unsorted_dir_block?(node) || unsorted_dir_each?(node)
end
unsorted_dir_pass?(node) click to toggle source
# File lib/rubocop/cop/lint/non_deterministic_require_order.rb, line 142
def unsorted_dir_pass?(node)
  unsorted_dir_glob_pass?(node) || unsorted_dir_each_pass?(node)
end