Commit 4072ded
Changed files (6)
lib/net/hippie/client.rb
@@ -26,13 +26,22 @@ module Net
def execute(uri, request, limit: follow_redirects, &block)
connection = connection_for(uri)
- response = connection.run(request)
- if limit.positive? && response.is_a?(Net::HTTPRedirection)
- url = connection.build_url_for(response['location'])
- request = request_for(Net::HTTP::Get, url)
- execute(url, request, limit: limit - 1, &block)
+ if block_given?
+ if block.arity == 2
+ response = connection.run(request)
+ yield(request, response)
+ else
+ connection.run(request, &block)
+ end
else
- block_given? ? yield(request, response) : response
+ response = connection.run(request)
+ if limit.positive? && response.is_a?(Net::HTTPRedirection)
+ url = connection.build_url_for(response['location'])
+ request = request_for(Net::HTTP::Get, url)
+ execute(url, request, limit: limit - 1)
+ else
+ response
+ end
end
end
lib/net/hippie/connection.rb
@@ -15,8 +15,14 @@ module Net
@http = http
end
- def run(request)
- @http.request(request)
+ def run(request, &block)
+ if block_given?
+ @http.request(request) do |response|
+ yield response
+ end
+ else
+ @http.request(request)
+ end
end
def build_url_for(path)
lib/net/hippie/version.rb
@@ -2,6 +2,6 @@
module Net
module Hippie
- VERSION = '1.3.0'
+ VERSION = '1.4.0'
end
end
test/net/client_test.rb
@@ -317,4 +317,42 @@ class ClientTest < Minitest::Test
end
end
end
+
+ def test_post_with_streaming
+ chunks = ["chunk1", "chunk2", "chunk3"]
+ response_body = chunks.join
+
+ WebMock.stub_request(:post, "https://example.org/stream")
+ .to_return(status: 200, body: response_body)
+
+ uri = URI.parse('https://example.org/stream')
+ received_chunks = []
+
+ subject.post(uri, body: { test: true }) do |response|
+ response.read_body do |chunk|
+ received_chunks << chunk
+ end
+ end
+
+ assert_equal chunks.join, received_chunks.join
+ end
+
+ def test_get_with_streaming
+ chunks = ["data1\n", "data2\n", "data3\n"]
+ response_body = chunks.join
+
+ WebMock.stub_request(:get, "https://example.org/stream")
+ .to_return(status: 200, body: response_body)
+
+ uri = URI.parse('https://example.org/stream')
+ received_data = []
+
+ subject.get(uri) do |response|
+ response.read_body do |chunk|
+ received_data << chunk
+ end
+ end
+
+ assert_equal chunks.join, received_data.join
+ end
end
CHANGELOG.md
@@ -8,6 +8,11 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
## [Unreleased]
+## [1.4.0] - 2025-10-08
+### Added
+- Streaming response support via block parameter
+- Backward compatible with existing block API (arity-based detection)
+
## [1.3.0] - 2025-04-30
### Changed
- Ruby 2.3+ required
@@ -95,7 +100,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
- with\_retry.
- authorization header helpers
-[Unreleased]: https://github.com/xlgmokha/net-hippie/compare/v1.3.0...HEAD
+[Unreleased]: https://github.com/xlgmokha/net-hippie/compare/v1.4.0...HEAD
+[1.4.0]: https://github.com/xlgmokha/net-hippie/compare/v1.3.0...v1.4.0
[1.3.0]: https://github.com/xlgmokha/net-hippie/compare/v1.2.0...v1.3.0
[1.2.0]: https://github.com/xlgmokha/net-hippie/compare/v1.1.1...v1.2.0
[1.1.1]: https://github.com/xlgmokha/net-hippie/compare/v1.1.0...v1.1.1
README.md
@@ -90,6 +90,40 @@ headers = { 'Authorization' => Net::Hippie.bearer_auth('token') }
Net::Hippie.get('https://www.example.org', headers: headers)
```
+### Streaming Responses
+
+Net::Hippie supports streaming responses by passing a block that accepts the response object:
+
+```ruby
+Net::Hippie.post('https://api.example.com/stream', body: { prompt: 'Hello' }) do |response|
+ response.read_body do |chunk|
+ print chunk
+ end
+end
+```
+
+This is useful for Server-Sent Events (SSE) or other streaming APIs:
+
+```ruby
+client = Net::Hippie::Client.new
+client.post('https://api.openai.com/v1/chat/completions',
+ headers: {
+ 'Authorization' => Net::Hippie.bearer_auth(ENV['OPENAI_API_KEY']),
+ 'Content-Type' => 'application/json'
+ },
+ body: { model: 'gpt-4', messages: [{ role: 'user', content: 'Hi' }], stream: true }
+) do |response|
+ buffer = ""
+ response.read_body do |chunk|
+ buffer += chunk
+ while (line = buffer.slice!(/.*\n/))
+ next if line.strip.empty?
+ puts line if line.start_with?('data: ')
+ end
+ end
+end
+```
+
## Development
After checking out the repo, run `bin/setup` to install dependencies. Then, run `bin/test` to run the tests.