class RuboCop::Cop::RSpec::ReceiveMessages
end
allow(Service).to receive(:foo).and_return(qux)
allow(Service).to receive(:foo).and_return(bar)
before do
# good - ignore same message
end
allow(Service).to receive_messages(foo: bar, baz: qux)
before do
# good
end
allow(Service).to receive(:baz).and_return(qux)
allow(Service).to receive(:foo).and_return(bar)
before do
# bad
@example
before they are defined.
order of stubs. This in turn may cause e.g. variables to be called
The autocorrection is marked as unsafe, because it may change the
@safety
Checks for multiple messages stubbed on the same object.
def add_repeated_lines_and_arguments(items)
def add_repeated_lines_and_arguments(items) uniq_items = uniq_items(items) repeated_lines = uniq_items.map(&:first_line) uniq_items.map do |item| [item, repeated_lines - [item.first_line], arguments(uniq_items)] end end
def arguments(items)
def arguments(items) items.map do |item| receive_and_return_argument(item) do |receive_arg, return_arg| "#{normalize_receive_arg(receive_arg)}: " \ "#{normalize_return_arg(return_arg)}" end end end
def heredoc_or_splat?(node)
def heredoc_or_splat?(node) (node.type?(:str, :dstr) && node.heredoc?) || node.splat_type? end
def item_range_by_whole_lines(item)
def item_range_by_whole_lines(item) range_by_whole_lines(item.source_range, include_final_newline: true) end
def message(repeated_lines)
def message(repeated_lines) format(MSG, loc: repeated_lines) end
def normalize_receive_arg(receive_arg)
def normalize_receive_arg(receive_arg) if requires_quotes?(receive_arg) "'#{receive_arg}'" else receive_arg end end
def normalize_return_arg(return_arg)
def normalize_return_arg(return_arg) if return_arg.hash_type? && !return_arg.braces? "{ #{return_arg.source} }" else return_arg.source end end
def on_begin(node)
def on_begin(node) repeated_receive_message(node).each do |item, repeated_lines, args| next if repeated_lines.empty? register_offense(item, repeated_lines, args) end end
def register_offense(item, repeated_lines, args)
def register_offense(item, repeated_lines, args) add_offense(item, message: message(repeated_lines)) do |corrector| if item.loc.line > repeated_lines.max replace_to_receive_messages(corrector, item, args) else corrector.remove(item_range_by_whole_lines(item)) end end end
def repeated_receive_message(node)
def repeated_receive_message(node) node .children .select { |child| allow_receive_message?(child) } .group_by { |child| allow_argument(child) } .values .reject(&:one?) .flat_map { |items| add_repeated_lines_and_arguments(items) } end
def replace_to_receive_messages(corrector, item, args)
def replace_to_receive_messages(corrector, item, args) receive_node(item) do |node| corrector.replace(node, "receive_messages(#{args.join(', ')})") end end
def requires_quotes?(value)
def requires_quotes?(value) value.match?(/^:".*?"|=$|^\W+$/) end
def uniq_items(items)
def uniq_items(items) items.select do |item| items.none? do |i| receive_arg(item).first == receive_arg(i).first && !same_line?(item, i) end end end