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
Source
# File lib/rubocop/cop/lint/non_deterministic_require_order.rb, line 68 def on_block(node) 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
Source
# File lib/rubocop/cop/lint/non_deterministic_require_order.rb, line 90 def on_block_pass(node) 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.last_argument&.block_pass_type? correct_block_pass(corrector, parent_node) else correct_block(corrector, parent_node) end end end
Source
# File lib/rubocop/cop/lint/non_deterministic_require_order.rb, line 79 def on_numblock(node) 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
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
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.last_argument 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
Source
# File lib/rubocop/cop/lint/non_deterministic_require_order.rb, line 132 def last_arg_range(node) node.last_argument.source_range.join(node.arguments[-2].source_range.end) end
Returns range of last argument including comma and whitespace.
@return [Parser::Source::Range]
Source
# File lib/rubocop/cop/lint/non_deterministic_require_order.rb, line 136 def unsorted_dir_loop?(node) unsorted_dir_block?(node) || unsorted_dir_each?(node) end
Source
# File lib/rubocop/cop/lint/non_deterministic_require_order.rb, line 140 def unsorted_dir_pass?(node) unsorted_dir_glob_pass?(node) || unsorted_dir_each_pass?(node) end