f_http_client 0.2.1 → 0.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 043b1047145e6431224027e68000eb4397224d1cd36da84766fa272aa9b472a2
4
- data.tar.gz: 660c261f27d5654909d338729ed8a8ad5166ec1038c978f05b3ba2ef6c0c0242
3
+ metadata.gz: 7bc7ea9df893eeb1677e578522732aba2ae7f6b4613bdd7621a5ab1e4686f48a
4
+ data.tar.gz: f471ba64951044aab190a13e29f93da1858c45c32d225268f23a4dd4324edde4
5
5
  SHA512:
6
- metadata.gz: 46118f4a70d1715d4c0fe3c86243f8ca80ccf2248c4c257b4b7ba69b3b3d089d6481e92299dcbf84f9675aacd5f8729a4df68037b156213dbcf4f32fc0cb35f8
7
- data.tar.gz: 146d096e199781aee21e17054994c93d9961886a454bc4bdbcabb45f9b0a4a12060c5d3149d991ff4b54ca1c96ee73320bcbd35d81114d91635c61ed690a1bba
6
+ metadata.gz: 2560bfd3e0954a0fa58dc416a76a6018ce3acd41afdb808f04742a56495aae7ee975c87421aeb2f5fd2d95762a8d616a6554653ff5210c06eeb5530d4a6a7c2e
7
+ data.tar.gz: '058d4e67c2ce215ebaa7cddf3fed92a2b964e962530849eb854c7c4af73528352aa4ee5341cb72a3531f11ae9618f6fa15df40691eccae767585ffb07484669b'
@@ -0,0 +1,3 @@
1
+ {
2
+ ".": "0.4.0"
3
+ }
data/.rubocop.yml CHANGED
@@ -1,9 +1,10 @@
1
- require:
1
+ plugins:
2
2
  - rubocop-rspec
3
3
  - rubocop-performance
4
+ - rubocop-vicenzo
4
5
 
5
6
  AllCops:
6
- TargetRubyVersion: 3.0
7
+ TargetRubyVersion: 3.2
7
8
  SuggestExtensions: false
8
9
  NewCops: enable
9
10
  CacheRootDirectory: tmp
@@ -15,6 +16,14 @@ AllCops:
15
16
 
16
17
  ##### LAYOUT #####
17
18
 
19
+ Layout/ArgumentAlignment:
20
+ Enabled: true
21
+ EnforcedStyle: with_fixed_indentation
22
+
23
+ Layout/FirstArgumentIndentation:
24
+ Enabled: true
25
+ EnforcedStyle: consistent
26
+
18
27
  Layout/FirstArrayElementIndentation:
19
28
  Enabled: true
20
29
  EnforcedStyle: consistent
@@ -23,10 +32,44 @@ Layout/FirstHashElementIndentation:
23
32
  Enabled: true
24
33
  EnforcedStyle: consistent
25
34
 
35
+ Layout/FirstMethodArgumentLineBreak:
36
+ Enabled: true
37
+
26
38
  Layout/LineLength:
27
39
  Max: 120
28
40
  AllowedPatterns: ['(\A|\s)#']
29
41
 
42
+ Layout/MultilineMethodArgumentLineBreaks:
43
+ Enabled: true
44
+
45
+ Layout/MultilineMethodCallBraceLayout:
46
+ Enabled: true
47
+ EnforcedStyle: new_line
48
+
49
+ Layout/MultilineMethodCallIndentation:
50
+ Enabled: true
51
+ EnforcedStyle: indented
52
+
53
+ Layout/FirstArrayElementLineBreak:
54
+ Enabled: true
55
+
56
+ Layout/MultilineArrayLineBreaks:
57
+ Enabled: true
58
+
59
+ Layout/MultilineArrayBraceLayout:
60
+ Enabled: true
61
+ EnforcedStyle: new_line
62
+
63
+ Layout/FirstHashElementLineBreak:
64
+ Enabled: true
65
+
66
+ Layout/MultilineHashKeyLineBreaks:
67
+ Enabled: true
68
+
69
+ Layout/MultilineHashBraceLayout:
70
+ Enabled: true
71
+ EnforcedStyle: new_line
72
+
30
73
  ##### STYLE #####
31
74
 
32
75
  Style/PreferredHashMethods:
@@ -57,9 +100,6 @@ Style/LambdaCall:
57
100
  Style/Documentation:
58
101
  Enabled: false
59
102
 
60
- Style/ClassAndModuleChildren:
61
- Enabled: false
62
-
63
103
  ##### LINT #####
64
104
 
65
105
  Lint/MissingSuper:
@@ -70,7 +110,7 @@ Lint/MissingSuper:
70
110
  Naming/InclusiveLanguage:
71
111
  Enabled: false
72
112
 
73
- Naming/PredicateName:
113
+ Naming/PredicatePrefix:
74
114
  ForbiddenPrefixes:
75
115
  - is_
76
116
  - have_
@@ -110,12 +150,38 @@ RSpec/ContextWording:
110
150
  RSpec/ExampleLength:
111
151
  Max: 20
112
152
 
113
- RSpec/FilePath:
114
- CustomTransform:
115
- FHTTPClient: f_http_client
153
+ RSpec/MultipleExpectations:
154
+ Exclude:
155
+ - 'spec/f_http_client/rspec/matchers/**/*'
156
+
157
+ # This directory contains specs for custom RSpec matchers.
158
+ # These examples intentionally use nested `expect { ... }.to raise_error(...)`
159
+ # to assert on matcher failure messages, which inherently requires more than
160
+ # one `expect` per example and conflicts with the MultipleExpectations cop.
161
+ #
162
+ # Using `:aggregate_failures` here is not an option, because it captures the
163
+ # inner expectation failure and re-raises an aggregated error, so the outer
164
+ # `raise_error` matcher no longer sees the original `ExpectationNotMetError`
165
+ # and the specs for the failure messages become impossible to express.
116
166
 
117
167
  RSpec/MultipleMemoizedHelpers:
118
168
  Enabled: false
119
169
 
120
170
  RSpec/NestedGroups:
121
171
  Enabled: false
172
+
173
+ RSpec/SpecFilePathFormat:
174
+ CustomTransform:
175
+ FHTTPClient: f_http_client
176
+
177
+ ##### RUBYGEMS #####
178
+ Gemspec/RequireMFA:
179
+ Enabled: false
180
+
181
+ ##### Vicenzo #####
182
+
183
+ Vicenzo/Style/MultilineMethodCallParentheses:
184
+ AllowedMethods:
185
+ - to
186
+ - not_to
187
+ - to_not
data/CHANGELOG.md CHANGED
@@ -1,4 +1,21 @@
1
- ## [Unreleased]
1
+ # Changelog
2
+
3
+ ## [0.4.0](https://github.com/Fretadao/f_http_client/compare/v0.3.0...v0.4.0) (2026-08-20)
4
+
5
+
6
+ ### ⚠ BREAKING CHANGES
7
+
8
+ * the FService matchers compare the type list for equality, so specs asserting have_failed_with(:unprocessable_entity, :client_error) now need have_failed_with(:unprocessable_content, :unprocessable_entity, :client_error). Runtime is unaffected — on_failure matches by inclusion. Seven specs across apps/router and apps/web need this adjustment.
9
+
10
+ ### Features
11
+
12
+ * expose RFC 9110 status type aliases for 422 and 413 [CU-86ak35ptc] ([#37](https://github.com/Fretadao/f_http_client/issues/37)) ([277ac96](https://github.com/Fretadao/f_http_client/commit/277ac966c1aad874839445066cc251d2f3ea09e5))
13
+
14
+ ## [0.3.0] - 2026-08-19
15
+
16
+ - Add `f_http_client_response_including` RSpec matcher for testing HTTParty::Response objects with nested matchers
17
+ - Fix Ruby 4.0+ compatibility by adding activesupport >= 7.2, ostruct, and csv dependencies
18
+ - Change Disable rubygems MFA checking #14
2
19
 
3
20
  ## [0.2.1] - 2023-09-27
4
21
 
data/CONTRIBUTING.md ADDED
@@ -0,0 +1,92 @@
1
+ # Contributing
2
+
3
+ ## Development setup
4
+
5
+ Run `bin/setup` once after cloning. It installs dependencies and points
6
+ `core.hooksPath` at `.githooks/`, enabling a `commit-msg` hook that enforces
7
+ Conventional Commits locally.
8
+
9
+ Run the linter and the tests the same way CI does:
10
+
11
+ ```bash
12
+ bundle exec rubocop -c .rubocop.yml --parallel
13
+ bundle exec rspec -fp
14
+ ```
15
+
16
+ ## Commit messages — Conventional Commits
17
+
18
+ This project follows [Conventional Commits](https://www.conventionalcommits.org/).
19
+ The version bump and the `CHANGELOG.md` are generated automatically from commit
20
+ messages, so the prefix matters:
21
+
22
+ | Type | When to use | Release effect |
23
+ |------|-------------|----------------|
24
+ | `feat:` | New capability | minor bump |
25
+ | `fix:` | Bug fix | patch bump |
26
+ | `feat!:` or a `BREAKING CHANGE:` footer | Backwards-incompatible change | minor bump while `0.x`, major from `1.0` on |
27
+ | `chore:` `ci:` `docs:` `style:` `test:` `refactor:` | Maintenance | no release |
28
+
29
+ Pull requests are squash-merged, so **the PR title becomes the commit message**
30
+ release-please reads. Get the title right even when the individual commits are
31
+ messy.
32
+
33
+ > One thing to keep in mind when changing result types: the FService matchers
34
+ > compare the type list for **equality**, not inclusion. Adding a type to an
35
+ > existing result — say a second alias for a status — breaks every consuming spec
36
+ > that matches the exact list (`have_failed_with(:conflict, :client_error)`), even
37
+ > though `on_failure(:conflict)` hooks keep working. Treat it as a breaking change.
38
+
39
+ ## Releasing (release-please)
40
+
41
+ Releases are automated with [release-please](https://github.com/googleapis/release-please);
42
+ there is no version to edit and no tag to create by hand.
43
+
44
+ 1. Every push to `master` creates or updates a **release PR** titled
45
+ `chore(master): release X.Y.Z`. It bumps `lib/f_http_client/version.rb`,
46
+ `.release-please-manifest.json` and `Gemfile.lock`, and updates `CHANGELOG.md`
47
+ from the Conventional Commits made since the last release.
48
+ 2. **Do not edit `CHANGELOG.md` by hand.** release-please owns it.
49
+ 3. **Merging the release PR** creates the git tag `vX.Y.Z` and a GitHub Release.
50
+ That triggers `.github/workflows/publish.yml`, which publishes the gem to
51
+ RubyGems through [Trusted Publishing](https://guides.rubygems.org/trusted-publishing/)
52
+ — OIDC, with no API key stored in this repository. Consumers resolve it normally:
53
+
54
+ ```ruby
55
+ gem 'f_http_client', '~> 0.3'
56
+ ```
57
+
58
+ 4. The release PR always reflects **everything on `master` since the last
59
+ release**. Cut releases promptly: merge the release PR before landing
60
+ unrelated work you don't want included.
61
+
62
+ > Why this exists: 0.3.0 was published to RubyGems in February 2026, but the tag
63
+ > `v0.3.0` was never created — the newest tag is still `v0.2.1`, from 2023. So the
64
+ > released code has no reference point in git: no compare link, no way to check out
65
+ > what shipped. Nothing in the manual process caught that, and it had happened
66
+ > silently for a whole release.
67
+
68
+ ## If the release PR is failing
69
+
70
+ release-please only rebuilds the release branch when a **releasable** commit
71
+ (`feat:` / `fix:`) lands on `master`. Non-releasable commits (`chore:`, `ci:`,
72
+ `docs:`, `style:`, `test:`) do **not** rebuild it, so the release branch can fall
73
+ behind `master` and its CI can run stale code.
74
+
75
+ When the release PR's CI is red:
76
+
77
+ 1. Push the fix to `master` through a normal PR. **Never** push directly to the
78
+ `release-please--…` branch — release-please overwrites it.
79
+ 2. If those fix commits are non-releasable (so the release PR won't refresh on its
80
+ own), **close the release PR and delete its branch**. On the next push to
81
+ `master`, release-please recreates it from the current `master`.
82
+ 3. Merge the freshly recreated release PR.
83
+
84
+ Never manually rebase or force-push the release-please branch.
85
+
86
+ ## If the gem was not published
87
+
88
+ The tag and the GitHub Release already exist, so publishing is retryable: fix the
89
+ cause and use **Re-run jobs** on the failed `Publish gem` workflow run. An
90
+ authentication failure points at the trusted publisher registration on RubyGems
91
+ (the repository and the workflow filename must match `publish.yml`), not at the
92
+ workflow itself.
data/Gemfile CHANGED
@@ -6,12 +6,17 @@ source 'https://rubygems.org'
6
6
  gemspec
7
7
 
8
8
  group :development do
9
+ gem 'benchmark' # Required by rubocop on Ruby 4+
10
+ gem 'racc' # Required by parser on Ruby 4+
9
11
  gem 'rubocop'
10
12
  gem 'rubocop-performance'
11
13
  gem 'rubocop-rspec'
14
+ gem 'rubocop-vicenzo'
12
15
  end
13
16
 
14
17
  group :test do
18
+ gem 'csv' # Required by httparty on Ruby 4+ (moved from stdlib)
19
+ gem 'ostruct' # Required by simplecov on Ruby 4+ (moved from stdlib)
15
20
  gem 'rspec', '~> 3.0'
16
21
  gem 'simplecov'
17
22
  gem 'webmock'
@@ -20,5 +25,5 @@ end
20
25
  group :development, :test do
21
26
  gem 'pry'
22
27
  gem 'pry-nav'
23
- gem 'rake', '~> 13.0'
28
+ gem 'rake', '~> 13.4'
24
29
  end
data/Gemfile.lock CHANGED
@@ -1,8 +1,8 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- f_http_client (0.2.1)
5
- activesupport
4
+ f_http_client (0.4.0)
5
+ activesupport (>= 7.2)
6
6
  addressable
7
7
  dry-configurable
8
8
  dry-initializer
@@ -12,113 +12,150 @@ PATH
12
12
  GEM
13
13
  remote: https://rubygems.org/
14
14
  specs:
15
- activesupport (7.0.8)
16
- concurrent-ruby (~> 1.0, >= 1.0.2)
15
+ activesupport (8.1.3.1)
16
+ base64
17
+ bigdecimal
18
+ concurrent-ruby (~> 1.0, >= 1.3.1)
19
+ connection_pool (>= 2.2.5)
20
+ drb
17
21
  i18n (>= 1.6, < 2)
22
+ json
23
+ logger (>= 1.4.2)
18
24
  minitest (>= 5.1)
19
- tzinfo (~> 2.0)
20
- addressable (2.8.1)
21
- public_suffix (>= 2.0.2, < 6.0)
22
- ast (2.4.2)
25
+ securerandom (>= 0.3)
26
+ tzinfo (~> 2.0, >= 2.0.5)
27
+ uri (>= 0.13.1)
28
+ addressable (2.9.0)
29
+ public_suffix (>= 2.0.2, < 8.0)
30
+ ast (2.4.3)
31
+ base64 (0.3.0)
32
+ benchmark (0.5.0)
33
+ bigdecimal (4.1.2)
23
34
  coderay (1.1.3)
24
- concurrent-ruby (1.2.2)
25
- crack (0.4.5)
35
+ concurrent-ruby (1.3.8)
36
+ connection_pool (3.0.2)
37
+ crack (1.0.1)
38
+ bigdecimal
26
39
  rexml
27
- diff-lcs (1.5.0)
28
- docile (1.4.0)
29
- dry-configurable (1.1.0)
30
- dry-core (~> 1.0, < 2)
40
+ csv (3.3.6)
41
+ diff-lcs (1.6.2)
42
+ drb (2.2.3)
43
+ dry-configurable (1.3.0)
44
+ dry-core (~> 1.1)
31
45
  zeitwerk (~> 2.6)
32
- dry-core (1.0.1)
46
+ dry-core (1.2.0)
33
47
  concurrent-ruby (~> 1.0)
48
+ logger
34
49
  zeitwerk (~> 2.6)
35
- dry-initializer (3.1.1)
36
- f_service (0.3.0)
37
- hashdiff (1.0.1)
38
- httparty (0.21.0)
50
+ dry-initializer (3.2.0)
51
+ f_service (0.4.1)
52
+ hashdiff (1.2.1)
53
+ httparty (0.24.2)
54
+ csv
39
55
  mini_mime (>= 1.0.0)
40
56
  multi_xml (>= 0.5.2)
41
- i18n (1.14.1)
57
+ i18n (1.15.2)
42
58
  concurrent-ruby (~> 1.0)
43
- json (2.6.3)
44
- method_source (1.0.0)
59
+ json (2.21.2)
60
+ language_server-protocol (3.17.0.6)
61
+ lint_roller (1.1.0)
62
+ logger (1.7.0)
63
+ method_source (1.1.0)
45
64
  mini_mime (1.1.5)
46
- minitest (5.20.0)
47
- multi_xml (0.6.0)
48
- parallel (1.22.1)
49
- parser (3.2.1.0)
65
+ minitest (6.0.6)
66
+ drb (~> 2.0)
67
+ prism (~> 1.5)
68
+ multi_xml (0.8.1)
69
+ bigdecimal (>= 3.1, < 5)
70
+ ostruct (0.6.3)
71
+ parallel (1.28.0)
72
+ parser (3.3.12.0)
50
73
  ast (~> 2.4.1)
74
+ racc
75
+ prism (1.9.0)
51
76
  pry (0.14.2)
52
77
  coderay (~> 1.1)
53
78
  method_source (~> 1.0)
54
79
  pry-nav (1.0.0)
55
80
  pry (>= 0.9.10, < 0.15)
56
- public_suffix (5.0.1)
81
+ public_suffix (7.0.5)
82
+ racc (1.8.1)
57
83
  rainbow (3.1.1)
58
- rake (13.0.6)
59
- regexp_parser (2.7.0)
60
- rexml (3.2.5)
61
- rspec (3.12.0)
62
- rspec-core (~> 3.12.0)
63
- rspec-expectations (~> 3.12.0)
64
- rspec-mocks (~> 3.12.0)
65
- rspec-core (3.12.1)
66
- rspec-support (~> 3.12.0)
67
- rspec-expectations (3.12.2)
84
+ rake (13.4.2)
85
+ regexp_parser (2.12.0)
86
+ rexml (3.4.4)
87
+ rspec (3.13.2)
88
+ rspec-core (~> 3.13.0)
89
+ rspec-expectations (~> 3.13.0)
90
+ rspec-mocks (~> 3.13.0)
91
+ rspec-core (3.13.6)
92
+ rspec-support (~> 3.13.0)
93
+ rspec-expectations (3.13.5)
68
94
  diff-lcs (>= 1.2.0, < 2.0)
69
- rspec-support (~> 3.12.0)
70
- rspec-mocks (3.12.3)
95
+ rspec-support (~> 3.13.0)
96
+ rspec-mocks (3.13.7)
71
97
  diff-lcs (>= 1.2.0, < 2.0)
72
- rspec-support (~> 3.12.0)
73
- rspec-support (3.12.0)
74
- rubocop (1.45.1)
98
+ rspec-support (~> 3.13.0)
99
+ rspec-support (3.13.7)
100
+ rubocop (1.89.0)
75
101
  json (~> 2.3)
76
- parallel (~> 1.10)
77
- parser (>= 3.2.0.0)
102
+ language_server-protocol (~> 3.17.0.2)
103
+ lint_roller (~> 1.1.0)
104
+ parallel (>= 1.10)
105
+ parser (>= 3.3.0.2)
78
106
  rainbow (>= 2.2.2, < 4.0)
79
- regexp_parser (>= 1.8, < 3.0)
80
- rexml (>= 3.2.5, < 4.0)
81
- rubocop-ast (>= 1.24.1, < 2.0)
107
+ regexp_parser (>= 2.9.3, < 3.0)
108
+ rubocop-ast (>= 1.49.0, < 2.0)
82
109
  ruby-progressbar (~> 1.7)
83
- unicode-display_width (>= 2.4.0, < 3.0)
84
- rubocop-ast (1.26.0)
85
- parser (>= 3.2.1.0)
86
- rubocop-capybara (2.17.1)
87
- rubocop (~> 1.41)
88
- rubocop-performance (1.16.0)
89
- rubocop (>= 1.7.0, < 2.0)
90
- rubocop-ast (>= 0.4.0)
91
- rubocop-rspec (2.18.1)
92
- rubocop (~> 1.33)
93
- rubocop-capybara (~> 2.17)
94
- ruby-progressbar (1.11.0)
95
- simplecov (0.22.0)
96
- docile (~> 1.1)
97
- simplecov-html (~> 0.11)
98
- simplecov_json_formatter (~> 0.1)
99
- simplecov-html (0.12.3)
100
- simplecov_json_formatter (0.1.4)
110
+ unicode-display_width (>= 2.4.0, < 4.0)
111
+ rubocop-ast (1.50.0)
112
+ parser (>= 3.3.7.2)
113
+ prism (~> 1.7)
114
+ rubocop-performance (1.27.0)
115
+ lint_roller (~> 1.1)
116
+ rubocop (>= 1.89.0, < 2.0)
117
+ rubocop-ast (>= 1.47.1, < 2.0)
118
+ rubocop-rspec (3.10.2)
119
+ lint_roller (~> 1.1)
120
+ regexp_parser (>= 2.0)
121
+ rubocop (~> 1.86, >= 1.86.2)
122
+ rubocop-vicenzo (0.6.0)
123
+ lint_roller (~> 1.1)
124
+ rubocop (>= 1.72.2)
125
+ rubocop-rspec (>= 3.5.0)
126
+ ruby-progressbar (1.13.0)
127
+ securerandom (0.4.1)
128
+ simplecov (1.1.1)
101
129
  tzinfo (2.0.6)
102
130
  concurrent-ruby (~> 1.0)
103
- unicode-display_width (2.4.2)
104
- webmock (3.18.1)
131
+ unicode-display_width (3.2.0)
132
+ unicode-emoji (~> 4.1)
133
+ unicode-emoji (4.2.0)
134
+ uri (1.1.1)
135
+ webmock (3.26.2)
105
136
  addressable (>= 2.8.0)
106
137
  crack (>= 0.3.2)
107
138
  hashdiff (>= 0.4.0, < 2.0.0)
108
- zeitwerk (2.6.12)
139
+ zeitwerk (2.7.4)
109
140
 
110
141
  PLATFORMS
142
+ x86_64-darwin-24
111
143
  x86_64-linux
112
144
 
113
145
  DEPENDENCIES
146
+ benchmark
147
+ csv
114
148
  f_http_client!
149
+ ostruct
115
150
  pry
116
151
  pry-nav
117
- rake (~> 13.0)
152
+ racc
153
+ rake (~> 13.4)
118
154
  rspec (~> 3.0)
119
155
  rubocop
120
156
  rubocop-performance
121
157
  rubocop-rspec
158
+ rubocop-vicenzo
122
159
  simplecov
123
160
  webmock
124
161
 
data/README.md CHANGED
@@ -93,13 +93,44 @@ Result examples:
93
93
  ```rb
94
94
  Person::Create.(name: 'Joe Vicenzo', birthdate: '2000-01-01')
95
95
  .and_then { |user| return redirect_to users_path(user.id) }
96
- .on_failure(:unprocessable_entity) { |errors| return render_action :new, locals: { errors: errors } }
96
+ .on_failure(:unprocessable_content) { |errors| return render_action :new, locals: { errors: errors } }
97
97
  .on_failure(:client_error) { |errors| render_action :new, warning: errors }
98
98
  .on_failure(:server_error) { |error| render_action :new, warning: ['Try again latter.'] }
99
99
  .on_failure(:server_error) { |error| render_action :new, warning: ['Server is busy. Try again latter.'] }
100
100
  .on_failure { |_error, type| render_action :new, warning: ["Unexpected error. Contact admin and talk about #{type} error."] }
101
101
  ```
102
102
 
103
+ ### Status names renamed by RFC 9110
104
+
105
+ The failure/success type comes from the `Net::HTTPResponse` class name, which still carries the
106
+ pre-RFC 9110 names for two status codes. Rack 3.1+ — and therefore every Rails app — already
107
+ canonicalized them. For those two codes the result carries **both** symbols as types, canonical
108
+ first, so either name matches:
109
+
110
+ | Status | Canonical type (Rack 3.1+) | Legacy type (still matches) |
111
+ |--------|----------------------------|-----------------------------|
112
+ | 422 | `:unprocessable_content` | `:unprocessable_entity` |
113
+ | 413 | `:content_too_large` | `:payload_too_large` |
114
+
115
+ ```rb
116
+ # A 422 response yields the types below, in this order:
117
+ # [:unprocessable_content, :unprocessable_entity, :client_error]
118
+
119
+ Person::Create.(name: 'Joe Vicenzo')
120
+ .on_failure(:unprocessable_content) { |errors| render_action :new, locals: { errors: errors } }
121
+ .on_failure(:unprocessable_entity) { |errors| render_action :new, locals: { errors: errors } }
122
+ ```
123
+
124
+ Prefer the canonical name: an untyped `on_failure` receives it as the `type`, and the legacy one is
125
+ kept only to ease the migration — it will be dropped in a future major release.
126
+
127
+ If you assert on types with the FService matchers, note that `have_failed_with` compares the whole
128
+ type list, so specs covering those two codes need the full trio:
129
+
130
+ ```rb
131
+ expect(result).to have_failed_with(:unprocessable_content, :unprocessable_entity, :client_error)
132
+ ```
133
+
103
134
  This gem uses the gem [HTTParty](https://github.com/jnunemaker/httparty) as base to perform requests.
104
135
  Then we can use any [example](https://github.com/jnunemaker/httparty/tree/master/examples) to implements the method make_request, for GET, POST, PUT, etc.
105
136
  The class *FHTTPClient::Base* provides the following options to help building the request:
@@ -109,6 +140,47 @@ The class *FHTTPClient::Base* provides the following options to help building th
109
140
  - *options*: can be used provide any other option for HTTParty;
110
141
  - *path_params*, can be used to fills params which is in the request path.
111
142
 
143
+ ## RSpec Matchers
144
+
145
+ ### `f_http_client_response_including`
146
+
147
+ Tests HTTParty::Response objects with support for nested RSpec matchers.
148
+
149
+ ```rb
150
+ require 'f_http_client/rspec'
151
+
152
+ RSpec.describe MyApi::Products::List do
153
+ it 'returns successful response with products' do
154
+ stub_request(:get, "https://api.example.com/products")
155
+ .to_return(status: 200, body: { products: [{id: 1}, {id: 2}] }.to_json)
156
+
157
+ service_result = described_class.()
158
+
159
+ # Use with FService matchers
160
+ expect(service_result)
161
+ .to have_succeed_with(:ok, :successful)
162
+ .and_value(f_http_client_response_including(products: have_attributes(size: 2), page: be_a(Integer)))
163
+ end
164
+
165
+ it 'supports nested matchers' do
166
+ stub_request(:get, "https://api.example.com/products")
167
+ .to_return(status: 200, body: { items: [{id: 1}] }.to_json)
168
+
169
+ response = described_class.().value
170
+
171
+ # Direct response testing
172
+ expect(response)
173
+ .to f_http_client_response_including(items: a_collection_containing_exactly(a_hash_including(id: 1)))
174
+ end
175
+ end
176
+ ```
177
+
178
+ The matcher:
179
+ - Automatically calls `parsed_response` on HTTParty::Response objects
180
+ - Supports any nested RSpec matchers (`have_attributes`, `a_hash_including`, `be_a`, etc.)
181
+ - Works seamlessly with FService's `.and_value()` and `.and_error()` matchers
182
+ - Provides clear failure messages showing expected vs actual
183
+
112
184
  ## Development
113
185
 
114
186
  After checking out the repo, run `bin/setup` to install dependencies. Then, run `rake spec` to run the tests.
@@ -0,0 +1,43 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative 'lib/f_http_client/version'
4
+
5
+ Gem::Specification.new do |spec|
6
+ spec.name = 'f_http_client'
7
+ spec.version = FHTTPClient::VERSION
8
+ spec.authors = ['Fretadao Tech Team']
9
+ spec.email = ['tech@fretadao.com.br']
10
+
11
+ spec.summary = 'Gem to provade a base for an HTTP client using FService architecture'
12
+ spec.homepage = 'https://github.com/Fretadao/f_http_client'
13
+ spec.license = 'MIT'
14
+ spec.required_ruby_version = '>= 3.2.0'
15
+
16
+ spec.metadata['homepage_uri'] = spec.homepage
17
+ spec.metadata['source_code_uri'] = spec.homepage
18
+ spec.metadata['changelog_uri'] = 'https://github.com/Fretadao/f_http_client/blob/master/CHANGELOG.md'
19
+
20
+ # Specify which files should be added to the gem when it is released.
21
+ # The `git ls-files -z` loads the files in the RubyGem that have been added into git.
22
+ spec.files = Dir.chdir(__dir__) do
23
+ `git ls-files -z`.split("\x0").reject do |f|
24
+ (f == __FILE__) || f.match(%r{\A(?:(?:bin|test|spec|features)/|\.(?:git|circleci)|appveyor)})
25
+ end
26
+ end
27
+ spec.bindir = 'exe'
28
+ spec.executables = spec.files.grep(%r{\Aexe/}) { |f| File.basename(f) }
29
+ spec.require_paths = ['lib']
30
+
31
+ # Uncomment to register a new dependency of your gem
32
+ # spec.add_dependency "example-gem", "~> 1.0"
33
+ spec.add_dependency 'activesupport', '>= 7.2'
34
+ spec.add_dependency 'addressable'
35
+ spec.add_dependency 'dry-configurable'
36
+ spec.add_dependency 'dry-initializer'
37
+ spec.add_dependency 'f_service', '>= 0.3.0'
38
+ spec.add_dependency 'httparty'
39
+
40
+ # For more information and examples about making a new gem, check out our
41
+ # guide at: https://bundler.io/guides/creating_gem.html
42
+ spec.metadata['rubygems_mfa_required'] = 'false'
43
+ end
@@ -23,7 +23,8 @@ module FHTTPClient
23
23
  FHTTPClient::Processor::Response.(response: response, log_strategy: log_strategy)
24
24
  end
25
25
  rescue StandardError => e
26
- FHTTPClient::Processor::Exception.(error: e, log_strategy: log_strategy)
26
+ FHTTPClient::Processor::Exception
27
+ .(error: e, log_strategy: log_strategy)
27
28
  .on_failure(:uncaught_error) { raise e }
28
29
  end
29
30
 
@@ -2,7 +2,8 @@
2
2
 
3
3
  module FHTTPClient
4
4
  module Cache
5
- RailsNotDefined = Class.new(StandardError)
5
+ class RailsNotDefined < StandardError
6
+ end
6
7
 
7
8
  class Rails
8
9
  class << self
@@ -31,6 +31,8 @@ module FHTTPClient
31
31
  :connection_refused
32
32
  when 'SocketError'
33
33
  :connection_error
34
+ when 'OpenSSL::SSL::SSLError'
35
+ :ssl_error
34
36
  when /timeout/i
35
37
  :timeout
36
38
  else
@@ -34,6 +34,24 @@ module FHTTPClient
34
34
 
35
35
  private_constant :STATUS_FAMILIES
36
36
 
37
+ # Status codes whose canonical name changed with RFC 9110. The type comes from
38
+ # Net::HTTPResponse::CODE_TO_OBJ, which still carries the pre-RFC names, while Rack
39
+ # 3.1+ (and therefore every Rails app consuming this gem) already canonicalized them.
40
+ # Emitting both symbols, canonical first, lets `on_failure` match either name so
41
+ # consumers can migrate at their own pace.
42
+ #
43
+ # TODO: Drop this constant and #response_types once net-http adopts the RFC 9110 names
44
+ # (or the status-to-type source stops being CODE_TO_OBJ) AND consumers have migrated
45
+ # off the legacy names. See Rack::Utils OBSOLETE_SYMBOL_MAPPINGS (rack >= 3.1).
46
+ RENAMED_TYPES = {
47
+ unprocessable_entity: %i[unprocessable_content unprocessable_entity],
48
+ unprocessable_content: %i[unprocessable_content unprocessable_entity],
49
+ payload_too_large: %i[content_too_large payload_too_large],
50
+ content_too_large: %i[content_too_large payload_too_large]
51
+ }.freeze
52
+
53
+ private_constant :RENAMED_TYPES
54
+
37
55
  def run
38
56
  log_data.and_then { success? ? success_response : failure_response }
39
57
  end
@@ -43,11 +61,24 @@ module FHTTPClient
43
61
  def_delegators :@response, :headers, :success?, :parsed_response, :code
44
62
 
45
63
  def success_response
46
- Success(response_type, response_family, data: response)
64
+ Success(*response_types, response_family, data: response)
47
65
  end
48
66
 
49
67
  def failure_response
50
- Failure(response_type, response_family, data: response)
68
+ Failure(*response_types, response_family, data: response)
69
+ end
70
+
71
+ # Private:
72
+ # Result types for the response status, from the canonical name to the legacy one.
73
+ # Ex: When the request returns a 422 (Unprocessable Content)
74
+ #
75
+ # # => [:unprocessable_content, :unprocessable_entity]
76
+ #
77
+ # Ex: When the request returns a 404 (Not Found)
78
+ #
79
+ # # => [:not_found]
80
+ def response_types
81
+ RENAMED_TYPES.fetch(response_type) { [response_type] }
51
82
  end
52
83
 
53
84
  # Private:
@@ -81,10 +112,10 @@ module FHTTPClient
81
112
  # # => "Unprocessable Entity"
82
113
  def message
83
114
  @message ||= response_class
84
- .to_s
85
- .delete_prefix('Net::HTTP')
86
- .gsub(/([A-Z]+)([A-Z][a-z])/, '\1 \2')
87
- .gsub(/([a-z])([A-Z])/, '\1 \2')
115
+ .to_s
116
+ .delete_prefix('Net::HTTP')
117
+ .gsub(/([A-Z]+)([A-Z][a-z])/, '\1 \2')
118
+ .gsub(/([a-z])([A-Z])/, '\1 \2')
88
119
  end
89
120
 
90
121
  # Private
@@ -0,0 +1,27 @@
1
+ # frozen_string_literal: true
2
+
3
+ RSpec::Matchers.define :f_http_client_response_including do |expected_hash|
4
+ match do |actual|
5
+ return false unless actual.respond_to?(:parsed_response)
6
+
7
+ @actual_parsed = actual.parsed_response
8
+
9
+ # Check if all expected key-value pairs are present in actual
10
+ expected_hash.all? do |expected_key, expected_value|
11
+ actual_value = @actual_parsed[expected_key]
12
+ # Use values_match? to support nested matchers
13
+ values_match?(expected_value, actual_value)
14
+ end
15
+ end
16
+
17
+ failure_message do |actual|
18
+ if actual.respond_to?(:parsed_response)
19
+ "expected HTTParty::Response parsed_response to include #{expected_hash.inspect}, " \
20
+ "but got #{@actual_parsed.inspect}"
21
+ else
22
+ "expected an HTTParty::Response, but got #{actual.class}"
23
+ end
24
+ end
25
+
26
+ diffable
27
+ end
@@ -0,0 +1,3 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative 'matchers/f_http_client_response_including'
@@ -1,3 +1,4 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require_relative 'rspec/support'
4
+ require_relative 'rspec/matchers'
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module FHTTPClient
4
- VERSION = '0.2.1'
4
+ VERSION = '0.4.0'
5
5
  end
data/lib/f_http_client.rb CHANGED
@@ -3,6 +3,7 @@
3
3
  require_relative 'f_http_client/version'
4
4
  require 'active_support/core_ext/array'
5
5
  require 'active_support/core_ext/enumerable'
6
+ require 'active_support/core_ext/object/blank'
6
7
  require 'active_support/core_ext/string'
7
8
  require 'addressable'
8
9
  require 'dry-configurable'
@@ -0,0 +1,18 @@
1
+ {
2
+ "$schema": "https://raw.githubusercontent.com/googleapis/release-please/main/schemas/config.json",
3
+ "bump-minor-pre-major": true,
4
+ "include-component-in-tag": false,
5
+ "packages": {
6
+ ".": {
7
+ "release-type": "ruby",
8
+ "package-name": "f_http_client",
9
+ "version-file": "lib/f_http_client/version.rb",
10
+ "extra-files": [
11
+ {
12
+ "type": "generic",
13
+ "path": "spec/f_http_client_spec.rb"
14
+ }
15
+ ]
16
+ }
17
+ }
18
+ }
metadata CHANGED
@@ -1,14 +1,13 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: f_http_client
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.2.1
4
+ version: 0.4.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Fretadao Tech Team
8
- autorequire:
9
8
  bindir: exe
10
9
  cert_chain: []
11
- date: 2023-10-06 00:00:00.000000000 Z
10
+ date: 1980-01-02 00:00:00.000000000 Z
12
11
  dependencies:
13
12
  - !ruby/object:Gem::Dependency
14
13
  name: activesupport
@@ -16,14 +15,14 @@ dependencies:
16
15
  requirements:
17
16
  - - ">="
18
17
  - !ruby/object:Gem::Version
19
- version: '0'
18
+ version: '7.2'
20
19
  type: :runtime
21
20
  prerelease: false
22
21
  version_requirements: !ruby/object:Gem::Requirement
23
22
  requirements:
24
23
  - - ">="
25
24
  - !ruby/object:Gem::Version
26
- version: '0'
25
+ version: '7.2'
27
26
  - !ruby/object:Gem::Dependency
28
27
  name: addressable
29
28
  requirement: !ruby/object:Gem::Requirement
@@ -94,22 +93,24 @@ dependencies:
94
93
  - - ">="
95
94
  - !ruby/object:Gem::Version
96
95
  version: '0'
97
- description:
98
96
  email:
99
97
  - tech@fretadao.com.br
100
98
  executables: []
101
99
  extensions: []
102
100
  extra_rdoc_files: []
103
101
  files:
102
+ - ".release-please-manifest.json"
104
103
  - ".rspec"
105
104
  - ".rubocop.yml"
106
105
  - CHANGELOG.md
106
+ - CONTRIBUTING.md
107
107
  - Gemfile
108
108
  - Gemfile.lock
109
109
  - LICENSE
110
110
  - README.md
111
111
  - Rakefile
112
112
  - examples/post_find.rb
113
+ - f_http_client.gemspec
113
114
  - lib/f_http_client.rb
114
115
  - lib/f_http_client/base.rb
115
116
  - lib/f_http_client/cache/http_response_analizer.rb
@@ -125,6 +126,8 @@ files:
125
126
  - lib/f_http_client/processor/exception.rb
126
127
  - lib/f_http_client/processor/response.rb
127
128
  - lib/f_http_client/rspec.rb
129
+ - lib/f_http_client/rspec/matchers.rb
130
+ - lib/f_http_client/rspec/matchers/f_http_client_response_including.rb
128
131
  - lib/f_http_client/rspec/support.rb
129
132
  - lib/f_http_client/rspec/support/helpers.rb
130
133
  - lib/f_http_client/rspec/support/helpers/fake_response.rb
@@ -132,6 +135,7 @@ files:
132
135
  - lib/f_http_client/service.rb
133
136
  - lib/f_http_client/store.rb
134
137
  - lib/f_http_client/version.rb
138
+ - release-please-config.json
135
139
  - sig/f_http_client.rbs
136
140
  homepage: https://github.com/Fretadao/f_http_client
137
141
  licenses:
@@ -141,7 +145,6 @@ metadata:
141
145
  source_code_uri: https://github.com/Fretadao/f_http_client
142
146
  changelog_uri: https://github.com/Fretadao/f_http_client/blob/master/CHANGELOG.md
143
147
  rubygems_mfa_required: 'false'
144
- post_install_message:
145
148
  rdoc_options: []
146
149
  require_paths:
147
150
  - lib
@@ -149,15 +152,14 @@ required_ruby_version: !ruby/object:Gem::Requirement
149
152
  requirements:
150
153
  - - ">="
151
154
  - !ruby/object:Gem::Version
152
- version: 3.0.0
155
+ version: 3.2.0
153
156
  required_rubygems_version: !ruby/object:Gem::Requirement
154
157
  requirements:
155
158
  - - ">="
156
159
  - !ruby/object:Gem::Version
157
160
  version: '0'
158
161
  requirements: []
159
- rubygems_version: 3.2.32
160
- signing_key:
162
+ rubygems_version: 3.6.9
161
163
  specification_version: 4
162
164
  summary: Gem to provade a base for an HTTP client using FService architecture
163
165
  test_files: []