From c0208dd8a58f7a6b36c394e5a535e0a2f1b01946 Mon Sep 17 00:00:00 2001 From: Bruno Vicenzo Date: Mon, 2 Feb 2026 14:05:48 -0300 Subject: [PATCH 1/6] Add f_http_client_response_including RSpec matcher MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Add new matcher to test HTTParty::Response objects - Supports nested RSpec matchers (have_attributes, a_hash_including, etc.) - Works with FService's .and_value() and .and_error() matchers - Provides clear failure messages - Fix Ruby 4.0+ compatibility: - Update activesupport to >= 7.2 - Add ostruct, csv, benchmark, racc gems - Update RuboCop config (plugins, rename PredicateName → PredicatePrefix) - Update documentation with usage examples --- .rubocop.yml | 24 ++- CHANGELOG.md | 2 + Gemfile | 4 + Gemfile.lock | 158 +++++++++++------- README.md | 45 +++++ f_http_client.gemspec | 12 +- lib/f_http_client/cache/rails.rb | 3 +- lib/f_http_client/rspec.rb | 1 + lib/f_http_client/rspec/matchers.rb | 3 + .../f_http_client_response_including.rb | 27 +++ spec/f_http_client/cache/rails_spec.rb | 4 +- .../f_http_client_response_including_spec.rb | 97 +++++++++++ 12 files changed, 305 insertions(+), 75 deletions(-) create mode 100644 lib/f_http_client/rspec/matchers.rb create mode 100644 lib/f_http_client/rspec/matchers/f_http_client_response_including.rb create mode 100644 spec/f_http_client/rspec/matchers/f_http_client_response_including_spec.rb diff --git a/.rubocop.yml b/.rubocop.yml index 64cad8f..f8c1915 100644 --- a/.rubocop.yml +++ b/.rubocop.yml @@ -1,4 +1,4 @@ -require: +plugins: - rubocop-rspec - rubocop-performance @@ -70,7 +70,7 @@ Lint/MissingSuper: Naming/InclusiveLanguage: Enabled: false -Naming/PredicateName: +Naming/PredicatePrefix: ForbiddenPrefixes: - is_ - have_ @@ -110,9 +110,19 @@ RSpec/ContextWording: RSpec/ExampleLength: Max: 20 -RSpec/FilePath: - CustomTransform: - FHTTPClient: f_http_client +RSpec/MultipleExpectations: + Exclude: + - 'spec/f_http_client/rspec/matchers/**/*' + + # This directory contains specs for custom RSpec matchers. + # These examples intentionally use nested `expect { ... }.to raise_error(...)` + # to assert on matcher failure messages, which inherently requires more than + # one `expect` per example and conflicts with the MultipleExpectations cop. + # + # Using `:aggregate_failures` here is not an option, because it captures the + # inner expectation failure and re-raises an aggregated error, so the outer + # `raise_error` matcher no longer sees the original `ExpectationNotMetError` + # and the specs for the failure messages become impossible to express. RSpec/MultipleMemoizedHelpers: Enabled: false @@ -120,6 +130,10 @@ RSpec/MultipleMemoizedHelpers: RSpec/NestedGroups: Enabled: false +RSpec/SpecFilePathFormat: + CustomTransform: + FHTTPClient: f_http_client + ##### RUBYGEMS ##### Gemspec/RequireMFA: Enabled: false diff --git a/CHANGELOG.md b/CHANGELOG.md index 205ee18..3d2c7e8 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,7 @@ ## [Unreleased] +- Add `f_http_client_response_including` RSpec matcher for testing HTTParty::Response objects with nested matchers +- Fix Ruby 4.0+ compatibility by adding activesupport >= 7.2, ostruct, and csv dependencies - Change Disable rubygems MFA checking #14 ## [0.2.1] - 2023-09-27 diff --git a/Gemfile b/Gemfile index 0e4390c..ab28c79 100644 --- a/Gemfile +++ b/Gemfile @@ -6,12 +6,16 @@ source 'https://rubygems.org' gemspec group :development do + gem 'benchmark' # Required by rubocop on Ruby 4+ + gem 'racc' # Required by parser on Ruby 4+ gem 'rubocop' gem 'rubocop-performance' gem 'rubocop-rspec' end group :test do + gem 'csv' # Required by httparty on Ruby 4+ (moved from stdlib) + gem 'ostruct' # Required by simplecov on Ruby 4+ (moved from stdlib) gem 'rspec', '~> 3.0' gem 'simplecov' gem 'webmock' diff --git a/Gemfile.lock b/Gemfile.lock index f6b2324..fdaf769 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -2,7 +2,7 @@ PATH remote: . specs: f_http_client (0.2.1) - activesupport + activesupport (>= 7.2) addressable dry-configurable dry-initializer @@ -12,108 +12,144 @@ PATH GEM remote: https://rubygems.org/ specs: - activesupport (7.0.8) - concurrent-ruby (~> 1.0, >= 1.0.2) + activesupport (8.1.2) + base64 + bigdecimal + concurrent-ruby (~> 1.0, >= 1.3.1) + connection_pool (>= 2.2.5) + drb i18n (>= 1.6, < 2) + json + logger (>= 1.4.2) minitest (>= 5.1) - tzinfo (~> 2.0) - addressable (2.8.1) - public_suffix (>= 2.0.2, < 6.0) - ast (2.4.2) + securerandom (>= 0.3) + tzinfo (~> 2.0, >= 2.0.5) + uri (>= 0.13.1) + addressable (2.8.8) + public_suffix (>= 2.0.2, < 8.0) + ast (2.4.3) + base64 (0.3.0) + benchmark (0.5.0) + bigdecimal (4.0.1) coderay (1.1.3) - concurrent-ruby (1.2.2) - crack (0.4.5) + concurrent-ruby (1.3.6) + connection_pool (3.0.2) + crack (1.0.1) + bigdecimal rexml - diff-lcs (1.5.0) - docile (1.4.0) - dry-configurable (1.1.0) - dry-core (~> 1.0, < 2) + csv (3.3.5) + diff-lcs (1.6.2) + docile (1.4.1) + drb (2.2.3) + dry-configurable (1.3.0) + dry-core (~> 1.1) zeitwerk (~> 2.6) - dry-core (1.0.1) + dry-core (1.2.0) concurrent-ruby (~> 1.0) + logger zeitwerk (~> 2.6) - dry-initializer (3.1.1) - f_service (0.3.0) - hashdiff (1.0.1) - httparty (0.21.0) + dry-initializer (3.2.0) + f_service (0.3.1) + hashdiff (1.2.1) + httparty (0.24.2) + csv mini_mime (>= 1.0.0) multi_xml (>= 0.5.2) - i18n (1.14.1) + i18n (1.14.8) concurrent-ruby (~> 1.0) - json (2.6.3) - method_source (1.0.0) + json (2.18.0) + language_server-protocol (3.17.0.5) + lint_roller (1.1.0) + logger (1.7.0) + method_source (1.1.0) mini_mime (1.1.5) - minitest (5.20.0) - multi_xml (0.6.0) - parallel (1.22.1) - parser (3.2.1.0) + minitest (6.0.1) + prism (~> 1.5) + multi_xml (0.8.1) + bigdecimal (>= 3.1, < 5) + ostruct (0.6.3) + parallel (1.27.0) + parser (3.3.10.1) ast (~> 2.4.1) + racc + prism (1.9.0) pry (0.14.2) coderay (~> 1.1) method_source (~> 1.0) pry-nav (1.0.0) pry (>= 0.9.10, < 0.15) - public_suffix (5.0.1) + public_suffix (7.0.2) + racc (1.8.1) rainbow (3.1.1) - rake (13.0.6) - regexp_parser (2.7.0) - rexml (3.4.2) - rspec (3.12.0) - rspec-core (~> 3.12.0) - rspec-expectations (~> 3.12.0) - rspec-mocks (~> 3.12.0) - rspec-core (3.12.1) - rspec-support (~> 3.12.0) - rspec-expectations (3.12.2) + rake (13.3.1) + regexp_parser (2.11.3) + rexml (3.4.4) + rspec (3.13.2) + rspec-core (~> 3.13.0) + rspec-expectations (~> 3.13.0) + rspec-mocks (~> 3.13.0) + rspec-core (3.13.6) + rspec-support (~> 3.13.0) + rspec-expectations (3.13.5) diff-lcs (>= 1.2.0, < 2.0) - rspec-support (~> 3.12.0) - rspec-mocks (3.12.3) + rspec-support (~> 3.13.0) + rspec-mocks (3.13.7) diff-lcs (>= 1.2.0, < 2.0) - rspec-support (~> 3.12.0) - rspec-support (3.12.0) - rubocop (1.45.1) + rspec-support (~> 3.13.0) + rspec-support (3.13.7) + rubocop (1.84.0) json (~> 2.3) + language_server-protocol (~> 3.17.0.2) + lint_roller (~> 1.1.0) parallel (~> 1.10) - parser (>= 3.2.0.0) + parser (>= 3.3.0.2) rainbow (>= 2.2.2, < 4.0) - regexp_parser (>= 1.8, < 3.0) - rexml (>= 3.2.5, < 4.0) - rubocop-ast (>= 1.24.1, < 2.0) + regexp_parser (>= 2.9.3, < 3.0) + rubocop-ast (>= 1.49.0, < 2.0) ruby-progressbar (~> 1.7) - unicode-display_width (>= 2.4.0, < 3.0) - rubocop-ast (1.26.0) - parser (>= 3.2.1.0) - rubocop-capybara (2.17.1) - rubocop (~> 1.41) - rubocop-performance (1.16.0) - rubocop (>= 1.7.0, < 2.0) - rubocop-ast (>= 0.4.0) - rubocop-rspec (2.18.1) - rubocop (~> 1.33) - rubocop-capybara (~> 2.17) - ruby-progressbar (1.11.0) + unicode-display_width (>= 2.4.0, < 4.0) + rubocop-ast (1.49.0) + parser (>= 3.3.7.2) + prism (~> 1.7) + rubocop-performance (1.26.1) + lint_roller (~> 1.1) + rubocop (>= 1.75.0, < 2.0) + rubocop-ast (>= 1.47.1, < 2.0) + rubocop-rspec (3.9.0) + lint_roller (~> 1.1) + rubocop (~> 1.81) + ruby-progressbar (1.13.0) + securerandom (0.4.1) simplecov (0.22.0) docile (~> 1.1) simplecov-html (~> 0.11) simplecov_json_formatter (~> 0.1) - simplecov-html (0.12.3) + simplecov-html (0.13.2) simplecov_json_formatter (0.1.4) tzinfo (2.0.6) concurrent-ruby (~> 1.0) - unicode-display_width (2.4.2) - webmock (3.18.1) + unicode-display_width (3.2.0) + unicode-emoji (~> 4.1) + unicode-emoji (4.2.0) + uri (1.1.1) + webmock (3.26.1) addressable (>= 2.8.0) crack (>= 0.3.2) hashdiff (>= 0.4.0, < 2.0.0) - zeitwerk (2.6.12) + zeitwerk (2.7.4) PLATFORMS + x86_64-darwin-24 x86_64-linux DEPENDENCIES + benchmark + csv f_http_client! + ostruct pry pry-nav + racc rake (~> 13.0) rspec (~> 3.0) rubocop diff --git a/README.md b/README.md index 5a9e4d6..1fa4595 100644 --- a/README.md +++ b/README.md @@ -109,6 +109,51 @@ The class *FHTTPClient::Base* provides the following options to help building th - *options*: can be used provide any other option for HTTParty; - *path_params*, can be used to fills params which is in the request path. +## RSpec Matchers + +### `f_http_client_response_including` + +Tests HTTParty::Response objects with support for nested RSpec matchers. + +```rb +require 'f_http_client/rspec' + +RSpec.describe MyApi::Products::List do + it 'returns successful response with products' do + stub_request(:get, "https://api.example.com/products") + .to_return(status: 200, body: { products: [{id: 1}, {id: 2}] }.to_json) + + service_result = described_class.() + + # Use with FService matchers + expect(service_result) + .to have_succeed_with(:ok, :successful) + .and_value(f_http_client_response_including( + products: have_attributes(size: 2), + page: be_a(Integer) + )) + end + + it 'supports nested matchers' do + stub_request(:get, "https://api.example.com/products") + .to_return(status: 200, body: { items: [{id: 1}] }.to_json) + + response = described_class.().value + + # Direct response testing + expect(response).to f_http_client_response_including( + items: a_collection_containing_exactly(a_hash_including(id: 1)) + ) + end +end +``` + +The matcher: +- Automatically calls `parsed_response` on HTTParty::Response objects +- Supports any nested RSpec matchers (`have_attributes`, `a_hash_including`, `be_a`, etc.) +- Works seamlessly with FService's `.and_value()` and `.and_error()` matchers +- Provides clear failure messages showing expected vs actual + ## Development After checking out the repo, run `bin/setup` to install dependencies. Then, run `rake spec` to run the tests. diff --git a/f_http_client.gemspec b/f_http_client.gemspec index 9813288..4ccae7f 100644 --- a/f_http_client.gemspec +++ b/f_http_client.gemspec @@ -30,12 +30,12 @@ Gem::Specification.new do |spec| # Uncomment to register a new dependency of your gem # spec.add_dependency "example-gem", "~> 1.0" - spec.add_runtime_dependency 'activesupport' - spec.add_runtime_dependency 'addressable' - spec.add_runtime_dependency 'dry-configurable' - spec.add_runtime_dependency 'dry-initializer' - spec.add_runtime_dependency 'f_service', '>= 0.3.0' - spec.add_runtime_dependency 'httparty' + spec.add_dependency 'activesupport', '>= 7.2' + spec.add_dependency 'addressable' + spec.add_dependency 'dry-configurable' + spec.add_dependency 'dry-initializer' + spec.add_dependency 'f_service', '>= 0.3.0' + spec.add_dependency 'httparty' # For more information and examples about making a new gem, check out our # guide at: https://bundler.io/guides/creating_gem.html diff --git a/lib/f_http_client/cache/rails.rb b/lib/f_http_client/cache/rails.rb index 2d2dc0c..b78f384 100644 --- a/lib/f_http_client/cache/rails.rb +++ b/lib/f_http_client/cache/rails.rb @@ -2,7 +2,8 @@ module FHTTPClient module Cache - RailsNotDefined = Class.new(StandardError) + class RailsNotDefined < StandardError + end class Rails class << self diff --git a/lib/f_http_client/rspec.rb b/lib/f_http_client/rspec.rb index eabc575..e554cab 100644 --- a/lib/f_http_client/rspec.rb +++ b/lib/f_http_client/rspec.rb @@ -1,3 +1,4 @@ # frozen_string_literal: true require_relative 'rspec/support' +require_relative 'rspec/matchers' diff --git a/lib/f_http_client/rspec/matchers.rb b/lib/f_http_client/rspec/matchers.rb new file mode 100644 index 0000000..ca28057 --- /dev/null +++ b/lib/f_http_client/rspec/matchers.rb @@ -0,0 +1,3 @@ +# frozen_string_literal: true + +require_relative 'matchers/f_http_client_response_including' diff --git a/lib/f_http_client/rspec/matchers/f_http_client_response_including.rb b/lib/f_http_client/rspec/matchers/f_http_client_response_including.rb new file mode 100644 index 0000000..525d910 --- /dev/null +++ b/lib/f_http_client/rspec/matchers/f_http_client_response_including.rb @@ -0,0 +1,27 @@ +# frozen_string_literal: true + +RSpec::Matchers.define :f_http_client_response_including do |expected_hash| + match do |actual| + return false unless actual.respond_to?(:parsed_response) + + @actual_parsed = actual.parsed_response + + # Check if all expected key-value pairs are present in actual + expected_hash.all? do |expected_key, expected_value| + actual_value = @actual_parsed[expected_key] + # Use values_match? to support nested matchers + values_match?(expected_value, actual_value) + end + end + + failure_message do |actual| + if actual.respond_to?(:parsed_response) + "expected HTTParty::Response parsed_response to include #{expected_hash.inspect}, " \ + "but got #{@actual_parsed.inspect}" + else + "expected an HTTParty::Response, but got #{actual.class}" + end + end + + diffable +end diff --git a/spec/f_http_client/cache/rails_spec.rb b/spec/f_http_client/cache/rails_spec.rb index f5a2bdf..6219117 100644 --- a/spec/f_http_client/cache/rails_spec.rb +++ b/spec/f_http_client/cache/rails_spec.rb @@ -62,7 +62,7 @@ context 'and skip if option is provided' do context 'but result can not be cached' do - let(:is_even) { ->(number) { number.even? } } + let(:is_even) { lambda(&:even?) } let(:options) { { expires_in: 3_600, skip_if: is_even } } it 'does not cache the value', :aggregate_failures do @@ -72,7 +72,7 @@ end context 'and result can be cached' do - let(:is_odd) { ->(number) { number.odd? } } + let(:is_odd) { lambda(&:odd?) } let(:options) { { expires_in: 3_600, skip_if: is_odd } } it 'caches the value', :aggregate_failures do diff --git a/spec/f_http_client/rspec/matchers/f_http_client_response_including_spec.rb b/spec/f_http_client/rspec/matchers/f_http_client_response_including_spec.rb new file mode 100644 index 0000000..cfe0ce3 --- /dev/null +++ b/spec/f_http_client/rspec/matchers/f_http_client_response_including_spec.rb @@ -0,0 +1,97 @@ +# frozen_string_literal: true + +require 'spec_helper' + +RSpec.describe 'RSpec::Matchers' do + describe '#f_http_client_response_including' do + context 'when actual is not an HTTParty::Response' do + let(:not_a_response) { { status: 'ok' } } + + it 'does not match' do + expect(not_a_response).not_to f_http_client_response_including(status: 'ok') + end + + it 'shows helpful error message' do + expect { expect(not_a_response).to f_http_client_response_including(status: 'ok') } + .to raise_error( + RSpec::Expectations::ExpectationNotMetError, + /expected an HTTParty::Response, but got Hash/ + ) + end + end + + context 'when actual is an HTTParty::Response' do + let(:response) { instance_double(HTTParty::Response, parsed_response: parsed_response_data) } + + context 'and informed value is a simple object' do + context 'but object is not equal to parsed_response' do + let(:parsed_response_data) { { status: 'ok', code: 200 } } + + it 'does not match when value differs' do + expect(response).not_to f_http_client_response_including(status: 'error') + end + + it 'does not match when key is missing' do + expect(response).not_to f_http_client_response_including(missing_key: 'value') + end + + it 'shows expected vs actual in error message' do + expect { expect(response).to f_http_client_response_including(status: 'error', code: 500) } + .to raise_error( + RSpec::Expectations::ExpectationNotMetError, + /expected HTTParty::Response parsed_response to include/ + ) + end + end + + context 'and object is equal to parsed_response' do + let(:parsed_response_data) { { status: 'ok', code: 200 } } + + it 'matches when all expected key-value pairs are present' do + expect(response).to f_http_client_response_including(status: 'ok', code: 200) + end + + it 'matches when subset of keys match' do + expect(response).to f_http_client_response_including(status: 'ok') + end + end + end + + context 'and informed value is a matcher' do + let(:parsed_response_data) do + { + products: [{ id: 1, name: 'Product A' }, { id: 2, name: 'Product B' }], + page: 1, + total: 2 + } + end + + context 'but matcher does not match parsed_response' do + it 'fails when nested matcher does not match' do + expect(response).not_to f_http_client_response_including(products: have_attributes(size: 3)) + end + end + + context 'and matcher matches parsed_response' do + it 'supports have_attributes matcher' do + expect(response).to f_http_client_response_including(products: have_attributes(size: 2)) + end + + it 'supports a_hash_including matcher' do + expect(response).to f_http_client_response_including( + products: a_collection_containing_exactly(a_hash_including(id: 1), a_hash_including(id: 2)) + ) + end + + it 'supports multiple nested matchers' do + expect(response).to f_http_client_response_including( + products: have_attributes(size: 2), + page: be_a(Integer), + total: be_positive + ) + end + end + end + end + end +end From 5c14bc6f3eddb12c7b2147d23ccc55391a12f5c8 Mon Sep 17 00:00:00 2001 From: Bruno Vicenzo Date: Mon, 2 Feb 2026 14:17:14 -0300 Subject: [PATCH 2/6] Add new require after updating gems --- lib/f_http_client.rb | 1 + 1 file changed, 1 insertion(+) diff --git a/lib/f_http_client.rb b/lib/f_http_client.rb index c98d1a9..a598067 100644 --- a/lib/f_http_client.rb +++ b/lib/f_http_client.rb @@ -3,6 +3,7 @@ require_relative 'f_http_client/version' require 'active_support/core_ext/array' require 'active_support/core_ext/enumerable' +require 'active_support/core_ext/object/blank' require 'active_support/core_ext/string' require 'addressable' require 'dry-configurable' From c586c75d2ec48498a8bd6789514bc0231d719278 Mon Sep 17 00:00:00 2001 From: Bruno Vicenzo Date: Mon, 2 Feb 2026 14:23:17 -0300 Subject: [PATCH 3/6] Use markdown comments to avoid unecessary text on final description --- .github/pull_request_template.md | 15 ++++++--------- 1 file changed, 6 insertions(+), 9 deletions(-) diff --git a/.github/pull_request_template.md b/.github/pull_request_template.md index 519ee6b..74cbe86 100644 --- a/.github/pull_request_template.md +++ b/.github/pull_request_template.md @@ -1,15 +1,12 @@ ## Tipo de alteração -Este MR implementa as seguintes alterações: -_(Deixe apenas os itens aplicáveis)_ +Este PR implementa as seguintes alterações: + -- :sparkles: **Nova feature** (alteração que adiciona funcionalidade). - -- :bug: **Correção de bug** (alteração que corrige um problema). - -- :boom: **Breaking change** (correção ou feature que causaria alterações na funcionalidade existente). - -- :heavy_check_mark: **Dívida técnica** (alteração que resolve dívidas técnicas no sistema como falta de testes para funcionalidades antigas ou refatorações). +- :sparkles: **Nova feature**. +- :bug: **Correção de bug**. +- :boom: **Breaking change**. +- :heavy_check_mark: **Dívida técnica**. ## Detalhes da solução From 2d19fdda0dc307584752f9a29827018783439d11 Mon Sep 17 00:00:00 2001 From: Bruno Vicenzo Date: Mon, 2 Feb 2026 14:23:34 -0300 Subject: [PATCH 4/6] Configure Ruby versions on CI --- .github/workflows/development.yml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/development.yml b/.github/workflows/development.yml index 0d9710c..7d370f3 100644 --- a/.github/workflows/development.yml +++ b/.github/workflows/development.yml @@ -14,9 +14,9 @@ jobs: strategy: matrix: ruby: - - '3.0.5' - - '3.1.3' - - '3.2.1' + - '3.2' + - '3.3' + - '4.0' steps: - uses: actions/checkout@v4 From 2336ee751915f48a5a7a48bfe3817c5d1c43192c Mon Sep 17 00:00:00 2001 From: Bruno Vicenzo Date: Mon, 2 Feb 2026 14:36:31 -0300 Subject: [PATCH 5/6] =?UTF-8?q?Add=20Fretad=C3=A3o=20RuboCop=20cops=20and?= =?UTF-8?q?=20apply=20formatting=20rules?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Add rubocop-vicenzo plugin - Configure layout cops for consistent argument and method call formatting - Apply new formatting rules to codebase --- .rubocop.yml | 54 +++++++++++++++++++++++-- Gemfile | 1 + Gemfile.lock | 5 +++ lib/f_http_client/base.rb | 3 +- lib/f_http_client/processor/response.rb | 8 ++-- spec/f_http_client/cache/rails_spec.rb | 2 +- spec/f_http_client/store_spec.rb | 12 ++++-- 7 files changed, 72 insertions(+), 13 deletions(-) diff --git a/.rubocop.yml b/.rubocop.yml index f8c1915..b5820a5 100644 --- a/.rubocop.yml +++ b/.rubocop.yml @@ -1,6 +1,7 @@ plugins: - rubocop-rspec - rubocop-performance + - rubocop-vicenzo AllCops: TargetRubyVersion: 3.0 @@ -15,6 +16,14 @@ AllCops: ##### LAYOUT ##### +Layout/ArgumentAlignment: + Enabled: true + EnforcedStyle: with_fixed_indentation + +Layout/FirstArgumentIndentation: + Enabled: true + EnforcedStyle: consistent + Layout/FirstArrayElementIndentation: Enabled: true EnforcedStyle: consistent @@ -23,10 +32,44 @@ Layout/FirstHashElementIndentation: Enabled: true EnforcedStyle: consistent +Layout/FirstMethodArgumentLineBreak: + Enabled: true + Layout/LineLength: Max: 120 AllowedPatterns: ['(\A|\s)#'] +Layout/MultilineMethodArgumentLineBreaks: + Enabled: true + +Layout/MultilineMethodCallBraceLayout: + Enabled: true + EnforcedStyle: new_line + +Layout/MultilineMethodCallIndentation: + Enabled: true + EnforcedStyle: indented + +Layout/FirstArrayElementLineBreak: + Enabled: true + +Layout/MultilineArrayLineBreaks: + Enabled: true + +Layout/MultilineArrayBraceLayout: + Enabled: true + EnforcedStyle: new_line + +Layout/FirstHashElementLineBreak: + Enabled: true + +Layout/MultilineHashKeyLineBreaks: + Enabled: true + +Layout/MultilineHashBraceLayout: + Enabled: true + EnforcedStyle: new_line + ##### STYLE ##### Style/PreferredHashMethods: @@ -57,9 +100,6 @@ Style/LambdaCall: Style/Documentation: Enabled: false -Style/ClassAndModuleChildren: - Enabled: false - ##### LINT ##### Lint/MissingSuper: @@ -137,3 +177,11 @@ RSpec/SpecFilePathFormat: ##### RUBYGEMS ##### Gemspec/RequireMFA: Enabled: false + +##### Vicenzo ##### + +Vicenzo/Style/MultilineMethodCallParentheses: + AllowedMethods: + - to + - not_to + - to_not diff --git a/Gemfile b/Gemfile index ab28c79..4352708 100644 --- a/Gemfile +++ b/Gemfile @@ -11,6 +11,7 @@ group :development do gem 'rubocop' gem 'rubocop-performance' gem 'rubocop-rspec' + gem 'rubocop-vicenzo' end group :test do diff --git a/Gemfile.lock b/Gemfile.lock index fdaf769..e6f7761 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -118,6 +118,10 @@ GEM rubocop-rspec (3.9.0) lint_roller (~> 1.1) rubocop (~> 1.81) + rubocop-vicenzo (0.3.0) + lint_roller (~> 1.1) + rubocop (>= 1.72.2) + rubocop-rspec (>= 3.5.0) ruby-progressbar (1.13.0) securerandom (0.4.1) simplecov (0.22.0) @@ -155,6 +159,7 @@ DEPENDENCIES rubocop rubocop-performance rubocop-rspec + rubocop-vicenzo simplecov webmock diff --git a/lib/f_http_client/base.rb b/lib/f_http_client/base.rb index 6007f7f..9aa4bc1 100644 --- a/lib/f_http_client/base.rb +++ b/lib/f_http_client/base.rb @@ -23,7 +23,8 @@ def run FHTTPClient::Processor::Response.(response: response, log_strategy: log_strategy) end rescue StandardError => e - FHTTPClient::Processor::Exception.(error: e, log_strategy: log_strategy) + FHTTPClient::Processor::Exception + .(error: e, log_strategy: log_strategy) .on_failure(:uncaught_error) { raise e } end diff --git a/lib/f_http_client/processor/response.rb b/lib/f_http_client/processor/response.rb index ecdcf4f..a747b7d 100644 --- a/lib/f_http_client/processor/response.rb +++ b/lib/f_http_client/processor/response.rb @@ -81,10 +81,10 @@ def response_family # # => "Unprocessable Entity" def message @message ||= response_class - .to_s - .delete_prefix('Net::HTTP') - .gsub(/([A-Z]+)([A-Z][a-z])/, '\1 \2') - .gsub(/([a-z])([A-Z])/, '\1 \2') + .to_s + .delete_prefix('Net::HTTP') + .gsub(/([A-Z]+)([A-Z][a-z])/, '\1 \2') + .gsub(/([a-z])([A-Z])/, '\1 \2') end # Private diff --git a/spec/f_http_client/cache/rails_spec.rb b/spec/f_http_client/cache/rails_spec.rb index 6219117..9321bde 100644 --- a/spec/f_http_client/cache/rails_spec.rb +++ b/spec/f_http_client/cache/rails_spec.rb @@ -28,7 +28,7 @@ allow(rails_cache).to receive(:read).with(cache_name).and_return(cached_value) end - context 'when result is cached' do + context 'and result is cached' do let(:options) { { expires_in: 3_600 } } let(:block) { -> { 10 * 10 } } let(:cached_value) { 'cached value' } diff --git a/spec/f_http_client/store_spec.rb b/spec/f_http_client/store_spec.rb index fcedbea..c4acaae 100644 --- a/spec/f_http_client/store_spec.rb +++ b/spec/f_http_client/store_spec.rb @@ -36,7 +36,8 @@ key: '/user/1', block: block_to_proccess, skip_if: skip_if - ).value + ) + .value expect(service_result).to eq(block_result) expect(FHTTPClient::Cache::Rails) @@ -54,7 +55,8 @@ key: '/user/1', block: block_to_proccess, expires_in: expires_in - ).value + ) + .value expect(service_result).to eq(block_result) expect(FHTTPClient::Cache::Rails) @@ -91,7 +93,8 @@ key: '/user/1', block: block_to_proccess, skip_if: skip_if - ).value + ) + .value expect(service_result).to eq(block_result) expect(FHTTPClient::Cache::Null) @@ -109,7 +112,8 @@ key: '/user/1', block: block_to_proccess, expires_in: expires_in - ).value + ) + .value expect(service_result).to eq(block_result) expect(FHTTPClient::Cache::Null) From e142801f4890068a57a95b3da4cb22230d321a08 Mon Sep 17 00:00:00 2001 From: Bruno Vicenzo Date: Mon, 2 Feb 2026 14:40:49 -0300 Subject: [PATCH 6/6] Refactor README examples to follow new RuboCop formatting rules --- README.md | 10 +++------- 1 file changed, 3 insertions(+), 7 deletions(-) diff --git a/README.md b/README.md index 1fa4595..7615183 100644 --- a/README.md +++ b/README.md @@ -128,10 +128,7 @@ RSpec.describe MyApi::Products::List do # Use with FService matchers expect(service_result) .to have_succeed_with(:ok, :successful) - .and_value(f_http_client_response_including( - products: have_attributes(size: 2), - page: be_a(Integer) - )) + .and_value(f_http_client_response_including(products: have_attributes(size: 2), page: be_a(Integer))) end it 'supports nested matchers' do @@ -141,9 +138,8 @@ RSpec.describe MyApi::Products::List do response = described_class.().value # Direct response testing - expect(response).to f_http_client_response_including( - items: a_collection_containing_exactly(a_hash_including(id: 1)) - ) + expect(response) + .to f_http_client_response_including(items: a_collection_containing_exactly(a_hash_including(id: 1))) end end ```