yobi-http 0.16.0 → 0.20.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: 3558906f242a7670158833610ad7b062bf25e308e4985804241bdba30a9d6968
4
- data.tar.gz: d96e600ccdf03ebcddb0c9ce14a3a2c141fb1a1ba2ffe04ee7cdb0a1a5383c7d
3
+ metadata.gz: ee3f871068b818f6f194f64edecbfe16bfb51c5d590445da566a8e6229bd3b28
4
+ data.tar.gz: 17e8538a544d0cdc95317180a7e11418460c26288ee5f77f770c52356c654c90
5
5
  SHA512:
6
- metadata.gz: 68ec04f8b731ebc0e9f508ed332d6414292963d1dbbd97aaba96a360e930ce3d1421812d11c1031305126633db9ad012fb2f4efa6e170aba4e09e3eeef37cf3e
7
- data.tar.gz: 61c69a1bfdc761df2c791396fb84a3ce5183bf24b71ffb4073fbebdbd1e86b3e5e6986fbeeb977a7c2225624d72d083270e585e0afe186030066b3dab05f0e6c
6
+ metadata.gz: 70b8027ba0bec021821bd8e50bcc216796e29650d3a4791082146b608293e5b7b088ac7352317c0a010f05e8438b92343c3b8e64e1450027888c4d86d9545f63
7
+ data.tar.gz: 1b5249c74c29c93de6ae4d6138281745b98eedb50d216e52b096248a331ff7ce552b7d53aae0e4da4d045339d7b66b92f12ef4c72ede58d1c0623aa2bb5e8e01
data/CHANGELOG.md CHANGED
@@ -1,5 +1,9 @@
1
1
  ## [Unreleased]
2
2
 
3
+ ## [0.20.0]
4
+
5
+ - Add support to --stream flag
6
+
3
7
  ## [0.6.0]
4
8
 
5
9
  - Add output to file feature
data/README.md CHANGED
@@ -2,29 +2,198 @@
2
2
  [![License: MIT](https://img.shields.io/badge/License-MIT-green.svg)](https://opensource.org/licenses/MIT)
3
3
  [![Maintainability](https://qlty.sh/gh/dvinciguerra/projects/yobi-http/maintainability.svg)](https://qlty.sh/gh/dvinciguerra/projects/yobi-http)
4
4
 
5
- # Yobi(呼び) Http Client
5
+ # :zap: Yobi(呼び) Http Client
6
6
 
7
- Yobi is a Ruby gem that provides a simple and efficient way to make HTTP requests. It is designed to be easy to use
8
- and flexible, allowing you to customize your requests as needed.
9
-
10
- Its a lightweight implementation of the HTTPie tool, which is a command-line HTTP client that allows you to make
11
- HTTP requests and view the responses in a human-friendly format.
7
+ Yobi (呼び) is a modern HTTP client for the command line, written in Ruby, that makes interacting with APIs simple, intuitive, and fun. Inspired by HTTPie, Yobi provides an elegant way to make HTTP requests with formatted and readable output.
12
8
 
13
9
  ![](./screenshot.png)
14
10
 
11
+ #### Main Features
12
+
13
+ * Simple and intuitive interface - Natural and easy-to-remember syntax
14
+ * Automatic formatting - JSON responses with syntax highlighting
15
+ * Multiple authentication types - Basic, Bearer, Digest, and more
16
+ * File download - Save responses directly to files
17
+ * Redirect tracking - Automatically follow HTTP redirects
18
+ * Customizable output - Control exactly what you want to see (headers, body, or both)
19
+ * Full HTTP support - GET, POST, PUT, DELETE, PATCH, HEAD, OPTIONS
20
+ * Offline mode - Prepare and review requests without sending them
21
+ * No heavy dependencies - Quick installation via RubyGems
22
+ * Raw mode - Raw output for integration with other tools
23
+
24
+
15
25
  ## Installation
16
26
 
27
+ #### Prerequisites
28
+
29
+ * Ruby >= 3.0.0
30
+ * RubyGems (usually included with Ruby)
31
+
32
+ #### Via RubyGems (Recommended)
33
+
34
+ ```bash
35
+ gem install yobi-http
36
+ ```
37
+
38
+ #### From Source Code
39
+
17
40
  ```bash
41
+ # Clone the repository
42
+ git clone https://github.com/dvinciguerra/yobi-http.git
43
+ cd yobi-http
44
+
45
+ # Install dependencies
46
+ bundle install
47
+
48
+ # Install the gem locally
49
+ bundle exec rake install```bash
18
50
  gem install yobi-http
19
51
  ```
20
52
 
21
- ## Usage
53
+ #### Check Installation
54
+
55
+ ```bash
56
+ yobi --version
57
+ ```
58
+
59
+ If you receive command not found: yobi:
60
+
61
+ * Ensure that the RubyGems bin directory is in your PATH.
62
+ * Try using gem which yobi-http to locate the installation.
63
+
64
+
65
+ ## Quick Guide
66
+
67
+ #### Basic Syntax
68
+
69
+ ```bash
70
+ yobi [METHOD] <url> [HEADER:VALUE] [key=value] [key::value]
71
+ ```
72
+
73
+ ### Instant Examples
74
+
75
+ ```bash
76
+ # Simple GET
77
+ yobi https://api.github.com/users/github
78
+
79
+ # POST with JSON
80
+ yobi POST https://jsonplaceholder.typicode.com/posts \
81
+ title="Hello" \
82
+ body="World" \
83
+ userId=1
84
+
85
+ # With authentication
86
+ yobi -A basic -a username:password https://api.example.com/data
87
+
88
+ # WITH custom header
89
+ yobi GET https://api.example.com/data \
90
+ Authorization:"Bearer your_token_here"
91
+
92
+ # Save response to file
93
+ yobi https://api.example.com/data -o response.json
94
+
95
+ # Use localhost shortcut
96
+ yobi :8080/api/items
97
+ # Expands to: http://localhost:8080/api/items
98
+ ```
99
+
100
+ **JSONPlaceholder API (Testing Service)**
101
+
102
+ ```bash
103
+ # Get all posts
104
+ yobi https://jsonplaceholder.typicode.com/posts
105
+
106
+ # Get specific post
107
+ yobi https://jsonplaceholder.typicode.com/posts/1
108
+
109
+ # Create new post
110
+ yobi POST https://jsonplaceholder.typicode.com/posts \
111
+ title="My new post" \
112
+ body="This is the content" \
113
+ userId=1
114
+
115
+ # Update post
116
+ yobi PUT https://jsonplaceholder.typicode.com/posts/1 \
117
+ title="Updated title"
118
+
119
+ # Delete post
120
+ yobi DELETE https://jsonplaceholder.typicode.com/posts/1
121
+ ```
122
+
123
+ **GitHub API**
124
+
125
+ ```bash
126
+ # User information
127
+ yobi https://api.github.com/users/dvinciguerra
128
+
129
+ # User repositories
130
+ yobi https://api.github.com/users/dvinciguerra/repos
131
+
132
+ # With authentication (personal token)
133
+ yobi -A bearer -a your_github_token https://api.github.com/user
134
+ ```
135
+
136
+ **HTTPBin (API Testing Tool)**
137
+
138
+ ```bash
139
+ # GET with query params
140
+ yobi https://httpbin.org/get name=John age=30
141
+
142
+ # POST with JSON
143
+ yobi POST https://httpbin.org/post name=John age:=30 active:=true
144
+
145
+ # Custom headers
146
+ yobi https://httpbin.org/headers \
147
+ "X-Custom:value" \
148
+ "User-Agent:Yobi/1.0"
149
+
150
+ # Basic authentication test
151
+ yobi https://httpbin.org/basic-auth/username/password \
152
+ -A basic -a username:password
153
+
154
+ # Simulate delay
155
+ yobi https://httpbin.org/delay/5
156
+
157
+ # Test timeout
158
+ yobi --timeout 2 https://httpbin.org/delay/10
159
+ ```
160
+
161
+ **Typical REST API**
162
+
163
+ ```bash
164
+ # List resources
165
+ yobi GET https://api.example.com/v1/users
166
+
167
+ # Create resource
168
+ yobi POST https://api.example.com/v1/users \
169
+ name="John Silva" \
170
+ email="john@example.com" \
171
+ department="IT"
172
+
173
+ # Update resource
174
+ yobi PUT https://api.example.com/v1/users/123 \
175
+ name="John da Silva" \
176
+ department="Development"
177
+
178
+ # Partial update
179
+ yobi PATCH https://api.example.com/v1/users/123 \
180
+ department="Management"
181
+
182
+ # Delete resource
183
+ yobi DELETE https://api.example.com/v1/users/123
184
+
185
+ # With pagination
186
+ yobi GET https://api.example.com/v1/users \
187
+ page:=1 \
188
+ limit:=10
189
+
190
+ # With filters
191
+ yobi GET https://api.example.com/v1/users \
192
+ department="IT" \
193
+ status="active"
194
+ ```
22
195
 
23
- Some examples of how to use yobi:
24
196
 
25
- - `yobi GET https://jsonplaceholder.typicode.com/posts/1` - Makes a GET request to the specified URL and prints the response.
26
- - `yobi POST https://jsonplaceholder.typicode.com/posts title="foo" body="bar" userId=1` - Makes a POST request to the specified URL with the given data and prints the response.
27
- - `yobi GET https://jsonplaceholder.typicode.com/posts/1 Authorization:"Bearer <token>"` - Makes a GET request to the specified URL with the given header and prints the response.
28
197
 
29
198
  ## Development
30
199
 
data/exe/yobi CHANGED
@@ -16,7 +16,7 @@ require "yobi"
16
16
  # parsing arguments
17
17
  @options = {
18
18
  print: "HB", auth: nil, auth_type: "basic", verbose: false, raw: false, offline: false, follow: false, debug: false,
19
- timeout: nil, download: false, output: nil, content_type: :json
19
+ timeout: nil, download: false, output: nil, content_type: :json, stream: false
20
20
  }
21
21
 
22
22
  parser = OptionParser.new do |opts|
@@ -38,6 +38,7 @@ parser = OptionParser.new do |opts|
38
38
 
39
39
  opts.on("-p", "--print FORMAT", "Specify the output format (e.g., 'H' for headers, 'B' for body)") do |format|
40
40
  @options[:print] = format || "HB" # default to printing both headers and body
41
+ @options[:print_set_explicitly] = true
41
42
  warn "[Argument Error] Unsupported format: #{format}" unless format.match?(/\A[HBhb]+\z/)
42
43
  end
43
44
 
@@ -53,6 +54,21 @@ parser = OptionParser.new do |opts|
53
54
  @options[:raw] = true
54
55
  end
55
56
 
57
+ opts.on("-h", "--headers", "Print only response headers") do
58
+ @options[:raw] = true
59
+ @options[:print] = "H"
60
+ end
61
+
62
+ opts.on("-b", "--body", "Print only response body") do
63
+ @options[:raw] = true
64
+ @options[:print] = "B"
65
+ end
66
+
67
+ opts.on("--status", "Print only response status code") do
68
+ @options[:raw] = true
69
+ @options[:print] = "status"
70
+ end
71
+
56
72
  opts.on("--form", "Send data as application/x-www-form-urlencoded instead of JSON") do
57
73
  @options[:content_type] = :form
58
74
  end
@@ -74,11 +90,6 @@ parser = OptionParser.new do |opts|
74
90
  @options[:content_type] = :json
75
91
  end
76
92
 
77
- opts.on("-h", "--help", "Print this help message") do
78
- puts opts
79
- exit
80
- end
81
-
82
93
  opts.on("--offline", "Run in offline mode (prepare request but do not send)") do
83
94
  @options[:offline] = true
84
95
  end
@@ -95,6 +106,12 @@ parser = OptionParser.new do |opts|
95
106
  @options[:timeout] = timeout.to_f
96
107
  end
97
108
 
109
+ opts.on("--stream", "Stream response body (chunked transfer encoding), printing each chunk as it arrives") do
110
+ @options[:stream] = true
111
+ # Default to body-only when streaming, unless the user already set --print explicitly
112
+ @options[:print] = "B" unless @options[:print_set_explicitly]
113
+ end
114
+
98
115
  opts.on("--download", "Download response body to a file specified by --output") do
99
116
  @options[:download] = true
100
117
  end
@@ -112,6 +129,11 @@ parser = OptionParser.new do |opts|
112
129
  puts "#{Yobi.name} v#{Yobi::VERSION}"
113
130
  exit
114
131
  end
132
+
133
+ opts.on("--help", "Print this help message") do
134
+ puts opts
135
+ exit
136
+ end
115
137
  end
116
138
 
117
139
  parser.parse!(ARGV)
@@ -132,12 +154,14 @@ Yobi.args.auth_header(headers, @options) if @options[:auth]
132
154
  Yobi::Http.request(method, url, data: data, headers: headers, query: query, options: @options) do |http, request|
133
155
  options = @options
134
156
 
135
- # follow redirects, offline, download or standard request mode
157
+ # follow redirects, offline, download, stream or standard request mode
136
158
  response =
137
159
  if options[:follow]
138
160
  Yobi::Http.follow_redirects(http.request(request), url, method, data, headers, options)
139
161
  elsif options[:offline]
140
162
  Yobi::Http.offline_mode(request, options)
163
+ elsif options[:stream]
164
+ Yobi::Http.stream(request, http, options)
141
165
  elsif options[:download]
142
166
  Yobi::Http.download(request, http, options)
143
167
  else
@@ -145,7 +169,9 @@ Yobi::Http.request(method, url, data: data, headers: headers, query: query, opti
145
169
  end
146
170
 
147
171
  # render output
148
- if options[:raw]
172
+ if options[:raw] && options[:print].downcase == "status"
173
+ print response.code
174
+ elsif options[:raw]
149
175
  Yobi::Renders::Raw.render(request, response, options)
150
176
  else
151
177
  Yobi::Renders::Colored.render(request, response, options)
data/lib/yobi/http.rb CHANGED
@@ -41,10 +41,16 @@ module Yobi
41
41
  request.body = data.to_json unless data.empty?
42
42
 
43
43
  yield(http, request) if block_given?
44
- rescue Net::OpenTimeout, Net::ReadTimeout => e
45
- warn "Request timed out: #{e.message}"
46
- exit 1
47
44
  end
45
+ rescue Net::OpenTimeout, Net::ReadTimeout => e
46
+ warn "Timeout: #{e.message}"
47
+ exit 1
48
+ rescue SocketError, Socket::ResolutionError => e
49
+ warn "Network error: #{e.message}"
50
+ exit 1
51
+ rescue StandardError => e
52
+ warn "Error: #{e.message}"
53
+ exit 1
48
54
  end
49
55
  # rubocop:enable Metrics/AbcSize, Metrics/MethodLength, Metrics/ParameterLists
50
56
 
@@ -86,6 +92,24 @@ module Yobi
86
92
  end
87
93
  # rubocop:enable Metrics/AbcSize
88
94
 
95
+ def stream(request, http, options)
96
+ http.request(request) do |response|
97
+ Yobi::Renders::Stream.render_headers(response, options)
98
+
99
+ response.read_body do |chunk|
100
+ Yobi::Renders::Stream.render_chunk(chunk, options)
101
+ end
102
+
103
+ Yobi::Renders::Stream.flush_buffer(options)
104
+ end
105
+
106
+ exit 0
107
+ rescue Interrupt
108
+ Yobi::Renders::Stream.flush_buffer(options)
109
+ $stdout.puts
110
+ exit 0
111
+ end
112
+
89
113
  # rubocop:disable Metrics/AbcSize
90
114
  def download(request, http, options)
91
115
  http.request(request) do |response|
@@ -0,0 +1,94 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Yobi
4
+ module Renders
5
+ # Render streaming (chunked) response chunks to the terminal as they arrive
6
+ module Stream
7
+ class << self
8
+ # Called once when the response headers are received
9
+ def render_headers(response, options = {})
10
+ return unless options[:print]&.include?("H")
11
+
12
+ if options[:raw]
13
+ $stdout.puts "HTTP/#{response.http_version} #{response.code} #{response.message}"
14
+ response.each_header { |key, value| $stdout.puts "#{key}: #{value}" }
15
+ $stdout.puts
16
+ else
17
+ header_md = "# HTTP/#{response.http_version} #{response.code} #{response.message}\n\n" \
18
+ "```yaml\n" \
19
+ "#{response.each_capitalized.to_h.sort.map { |k, v| "#{k}: #{v}" }.join("\n")}\n" \
20
+ "```\n"
21
+ $stdout.print TTY::Markdown.parse(header_md, color: :always)
22
+ end
23
+
24
+ $stdout.flush
25
+ end
26
+
27
+ # Called for each chunk received. Buffers partial lines across calls so
28
+ # that JSON objects split across chunk boundaries are handled correctly.
29
+ def render_chunk(chunk, options = {})
30
+ return if chunk.nil? || chunk.empty?
31
+ return unless options[:print]&.include?("B")
32
+
33
+ if options[:raw]
34
+ $stdout.print chunk
35
+ $stdout.flush
36
+ return
37
+ end
38
+
39
+ data = (@line_buffer || "") + chunk
40
+ @line_buffer = nil
41
+
42
+ lines = data.split("\n", -1)
43
+
44
+ @line_buffer = lines.pop unless data.end_with?("\n")
45
+
46
+ lines.each do |line|
47
+ next if line.empty?
48
+
49
+ parsed = begin
50
+ JSON.parse(line)
51
+ rescue JSON::ParserError
52
+ nil
53
+ end
54
+
55
+ if parsed
56
+ pretty = JSON.pretty_generate(parsed)
57
+ md = "```json\n#{pretty}\n```\n"
58
+ $stdout.print TTY::Markdown.parse(md, color: :always)
59
+ else
60
+ $stdout.puts line
61
+ end
62
+
63
+ $stdout.flush
64
+ end
65
+ end
66
+
67
+ # Flush any remaining buffered line when the connection closes
68
+ def flush_buffer(options = {})
69
+ return unless @line_buffer && !@line_buffer.empty?
70
+ return unless options[:print]&.include?("B")
71
+
72
+ line = @line_buffer
73
+ @line_buffer = nil
74
+
75
+ parsed = begin
76
+ JSON.parse(line)
77
+ rescue JSON::ParserError
78
+ nil
79
+ end
80
+
81
+ if parsed && !options[:raw]
82
+ pretty = JSON.pretty_generate(parsed)
83
+ md = "```json\n#{pretty}\n```\n"
84
+ $stdout.print TTY::Markdown.parse(md, color: :always)
85
+ else
86
+ $stdout.puts line
87
+ end
88
+
89
+ $stdout.flush
90
+ end
91
+ end
92
+ end
93
+ end
94
+ end
data/lib/yobi/version.rb CHANGED
@@ -2,5 +2,5 @@
2
2
 
3
3
  module Yobi
4
4
  # Yobi gem version
5
- VERSION = "0.16.0"
5
+ VERSION = "0.20.0"
6
6
  end
metadata CHANGED
@@ -1,13 +1,13 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: yobi-http
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.16.0
4
+ version: 0.20.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Daniel Vinciguerra
8
8
  bindir: exe
9
9
  cert_chain: []
10
- date: 2026-03-05 00:00:00.000000000 Z
10
+ date: 1980-01-02 00:00:00.000000000 Z
11
11
  dependencies:
12
12
  - !ruby/object:Gem::Dependency
13
13
  name: base64
@@ -77,6 +77,7 @@ files:
77
77
  - lib/yobi/renders.rb
78
78
  - lib/yobi/renders/colored.rb
79
79
  - lib/yobi/renders/raw.rb
80
+ - lib/yobi/renders/stream.rb
80
81
  - lib/yobi/ui.rb
81
82
  - lib/yobi/ui/progress.rb
82
83
  - lib/yobi/version.rb
@@ -108,7 +109,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
108
109
  - !ruby/object:Gem::Version
109
110
  version: '0'
110
111
  requirements: []
111
- rubygems_version: 3.6.2
112
+ rubygems_version: 4.0.3
112
113
  specification_version: 4
113
114
  summary: Yobi is a terminal tool to make HTTP requests and display responses in a
114
115
  friendly way inspired by HTTPie.