From 8773d378e30ec1d987a5ed12c03e3eb9dd8cc2ba Mon Sep 17 00:00:00 2001 From: r7kamura Date: Fri, 10 Nov 2023 21:26:46 +0900 Subject: [PATCH] Support `Nokogiri::HTML.parse` and `Nokogiri::HTML5.parse` on `Rails/ResponseParsedBody` --- .../change_support_nokogiri_html_parse.md | 1 + config/default.yml | 2 +- lib/rubocop/cop/rails/response_parsed_body.rb | 62 ++++++++++++++++--- .../cop/rails/response_parsed_body_spec.rb | 34 ++++++++++ 4 files changed, 88 insertions(+), 11 deletions(-) create mode 100644 changelog/change_support_nokogiri_html_parse.md diff --git a/changelog/change_support_nokogiri_html_parse.md b/changelog/change_support_nokogiri_html_parse.md new file mode 100644 index 0000000000..3e153e7248 --- /dev/null +++ b/changelog/change_support_nokogiri_html_parse.md @@ -0,0 +1 @@ +* [#1181](https://github.com/rubocop/rubocop-rails/pull/1181): Support `Nokogiri::HTML.parse` and `Nokogiri::HTML5.parse` on `Rails/ResponseParsedBody`. ([@r7kamura][]) diff --git a/config/default.yml b/config/default.yml index c20ae180e0..33f4a9f284 100644 --- a/config/default.yml +++ b/config/default.yml @@ -902,7 +902,7 @@ Rails/RequireDependency: VersionAdded: '2.10' Rails/ResponseParsedBody: - Description: Prefer `response.parsed_body` to `JSON.parse(response.body)`. + Description: Prefer `response.parsed_body` to custom parsing logic for `response.body`. Enabled: pending Safe: false VersionAdded: '2.18' diff --git a/lib/rubocop/cop/rails/response_parsed_body.rb b/lib/rubocop/cop/rails/response_parsed_body.rb index d80b2ec06b..184ddcb3a7 100644 --- a/lib/rubocop/cop/rails/response_parsed_body.rb +++ b/lib/rubocop/cop/rails/response_parsed_body.rb @@ -3,25 +3,30 @@ module RuboCop module Cop module Rails - # Prefer `response.parsed_body` to `JSON.parse(response.body)`. + # Prefer `response.parsed_body` to custom parsing logic for `response.body`. # # @safety - # This cop is unsafe because Content-Type may not be `application/json`. For example, the proprietary - # Content-Type provided by corporate entities such as `application/vnd.github+json` is not supported at - # `response.parsed_body` by default, so you still have to use `JSON.parse(response.body)` there. + # This cop is unsafe because Content-Type may not be `application/json` or `text/html`. + # For example, the proprietary Content-Type provided by corporate entities such as + # `application/vnd.github+json` is not supported at `response.parsed_body` by default, + # so you still have to use `JSON.parse(response.body)` there. # # @example # # bad # JSON.parse(response.body) # + # # bad + # Nokogiri::HTML.parse(response.body) + # + # # bad + # Nokogiri::HTML5.parse(response.body) + # # # good # response.parsed_body class ResponseParsedBody < Base extend AutoCorrector extend TargetRailsVersion - MSG = 'Prefer `response.parsed_body` to `JSON.parse(response.body)`.' - RESTRICT_ON_SEND = %i[parse].freeze minimum_target_rails_version 5.0 @@ -38,12 +43,27 @@ class ResponseParsedBody < Base ) PATTERN + # @!method nokogiri_html_parse_response_body(node) + def_node_matcher :nokogiri_html_parse_response_body, <<~PATTERN + (send + (const + (const {nil? cbase} :Nokogiri) + ${:HTML :HTML5} + ) + :parse + (send + (send nil? :response) + :body + ) + ) + PATTERN + def on_send(node) - return unless json_parse_response_body?(node) + check_json_parse_response_body(node) - add_offense(node) do |corrector| - autocorrect(corrector, node) - end + return unless target_rails_version >= 7.1 + + check_nokogiri_html_parse_response_body(node) end private @@ -51,6 +71,28 @@ def on_send(node) def autocorrect(corrector, node) corrector.replace(node, 'response.parsed_body') end + + def check_json_parse_response_body(node) + return unless json_parse_response_body?(node) + + add_offense( + node, + message: 'Prefer `response.parsed_body` to `JSON.parse(response.body)`.' + ) do |corrector| + autocorrect(corrector, node) + end + end + + def check_nokogiri_html_parse_response_body(node) + return unless (const = nokogiri_html_parse_response_body(node)) + + add_offense( + node, + message: "Prefer `response.parsed_body` to `Nokogiri::#{const}.parse(response.body)`." + ) do |corrector| + autocorrect(corrector, node) + end + end end end end diff --git a/spec/rubocop/cop/rails/response_parsed_body_spec.rb b/spec/rubocop/cop/rails/response_parsed_body_spec.rb index bf36455b6c..fac0c61c19 100644 --- a/spec/rubocop/cop/rails/response_parsed_body_spec.rb +++ b/spec/rubocop/cop/rails/response_parsed_body_spec.rb @@ -42,4 +42,38 @@ RUBY end end + + context 'when `Nokogiri::HTML.parse(response.body)` is used on Rails 7.0', :rails70 do + it 'registers no offense' do + expect_no_offenses(<<~RUBY) + Nokogiri::HTML.parse(response.body) + RUBY + end + end + + context 'when `Nokogiri::HTML.parse(response.body)` is used on Rails 7.1', :rails71 do + it 'registers offense' do + expect_offense(<<~RUBY) + Nokogiri::HTML.parse(response.body) + ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Prefer `response.parsed_body` to `Nokogiri::HTML.parse(response.body)`. + RUBY + + expect_correction(<<~RUBY) + response.parsed_body + RUBY + end + end + + context 'when `Nokogiri::HTML5.parse(response.body)` is used on Rails 7.1', :rails71 do + it 'registers offense' do + expect_offense(<<~RUBY) + Nokogiri::HTML5.parse(response.body) + ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Prefer `response.parsed_body` to `Nokogiri::HTML5.parse(response.body)`. + RUBY + + expect_correction(<<~RUBY) + response.parsed_body + RUBY + end + end end