yobi-http 0.10.0 → 0.12.1
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 +4 -4
- data/Examples.md +72 -0
- data/MIT-LICENSE +21 -0
- data/exe/yobi +23 -71
- data/lib/yobi/http.rb +70 -2
- data/lib/yobi/renders/colored.rb +31 -0
- data/lib/yobi/renders/raw.rb +96 -0
- data/lib/yobi/renders.rb +5 -0
- data/lib/yobi/ui/progress.rb +80 -0
- data/lib/yobi/ui.rb +5 -0
- data/lib/yobi/version.rb +1 -1
- data/lib/yobi.rb +2 -0
- metadata +25 -7
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: fb20dc471298499f2ba3baa43cd51e83039cf67935544fe1f0299de33e709b4c
|
|
4
|
+
data.tar.gz: 9df3715695a95f5c23b506f1568b4bc42d3ecbe1e031bc6aab6e20d69b482a5d
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: b7761b5b83c95b4f8dafcadb20e9c7ab7463fc93f2c4d9334e2de37516ac5990ba5915deffc1dbce0a12f7ceb390d4a4b62b796590b3c966975dbe382ccefb8c
|
|
7
|
+
data.tar.gz: 342112ecb2fe37dbe4ce278353850207db149eef0cf1bfd088f4d1d646a563771756b481b3eaa76d83a081d3f2e9bea0ebb0457654d95543a2c9c9f0ac024c35
|
data/Examples.md
ADDED
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
|
|
2
|
+
# Yobi HTTP - Usage examples
|
|
3
|
+
|
|
4
|
+
Some examples of using the
|
|
5
|
+
|
|
6
|
+
Some examples of using the Yobi HTTP CLI.
|
|
7
|
+
|
|
8
|
+
## Simple HTTP Get request
|
|
9
|
+
|
|
10
|
+
```bash
|
|
11
|
+
yobi --debug https://httpbin.org/get
|
|
12
|
+
```
|
|
13
|
+
|
|
14
|
+
## HTTP POST request with JSON body
|
|
15
|
+
|
|
16
|
+
```bash
|
|
17
|
+
yobi --debug POST https://httpbin.org/post name=Yobi type="HTTP client"
|
|
18
|
+
```
|
|
19
|
+
|
|
20
|
+
## WIP: HTTP POST request with form data
|
|
21
|
+
|
|
22
|
+
```bash
|
|
23
|
+
yobi --debug POST https://httpbin.org/post name=Yobi type="HTTP client" --form
|
|
24
|
+
```
|
|
25
|
+
|
|
26
|
+
## HTTP request with query parameters
|
|
27
|
+
|
|
28
|
+
```bash
|
|
29
|
+
yobi --debug https://httpbin.org/get name=Yobi type="HTTP client"
|
|
30
|
+
```
|
|
31
|
+
## HTTP request with basic authentication
|
|
32
|
+
|
|
33
|
+
```bash
|
|
34
|
+
yobi --debug https://httpbin.org/basic-auth/user/passwd --auth user:passwd --auth-type basic
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
## HTTP request with timeout
|
|
38
|
+
|
|
39
|
+
```bash
|
|
40
|
+
yobi --debug --timeout 5 https://httpbin.org/delay/10
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
## HTTP request with custom headers
|
|
44
|
+
|
|
45
|
+
```bash
|
|
46
|
+
yobi --debug https://httpbin.org/headers X-Custom-Header:Yobi User-Agent:"Yobi HTTP Client"
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
## Download a file
|
|
50
|
+
|
|
51
|
+
```bash
|
|
52
|
+
yobi --debug --download https://ash-speed.hetzner.com/100MB.bin -o sample.bin
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
## HTTP request with follow redirects
|
|
56
|
+
|
|
57
|
+
```bash
|
|
58
|
+
yobi --debug --follow https://httpbin.org/redirect/3
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
## HTTP request with output to a file
|
|
62
|
+
|
|
63
|
+
```bash
|
|
64
|
+
yobi -p B https://httpbin.org/get -o response.json
|
|
65
|
+
```
|
|
66
|
+
|
|
67
|
+
## HTTP request with verbose output
|
|
68
|
+
|
|
69
|
+
```bash
|
|
70
|
+
yobi --debug --verbose https://httpbin.org/get
|
|
71
|
+
```
|
|
72
|
+
|
data/MIT-LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
Copyright (c) Daniel Vinciguerra
|
|
2
|
+
|
|
3
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
|
4
|
+
a copy of this software and associated documentation files (the
|
|
5
|
+
"Software"), to deal in the Software without restriction, including
|
|
6
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
|
7
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
|
8
|
+
permit persons to whom the Software is furnished to do so, subject to
|
|
9
|
+
the following conditions:
|
|
10
|
+
|
|
11
|
+
The above copyright notice and this permission notice shall be
|
|
12
|
+
included in all copies or substantial portions of the Software.
|
|
13
|
+
|
|
14
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
|
15
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
|
16
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
|
17
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
|
18
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
|
19
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
|
20
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
|
21
|
+
|
data/exe/yobi
CHANGED
|
@@ -15,7 +15,8 @@ require "yobi"
|
|
|
15
15
|
|
|
16
16
|
# parsing arguments
|
|
17
17
|
@options = {
|
|
18
|
-
print: "HB", auth: nil, auth_type: "basic", verbose: false, raw: false, offline: false, follow: false, debug: false
|
|
18
|
+
print: "HB", auth: nil, auth_type: "basic", verbose: false, raw: false, offline: false, follow: false, debug: false,
|
|
19
|
+
timeout: nil, download: false
|
|
19
20
|
}
|
|
20
21
|
|
|
21
22
|
parser = OptionParser.new do |opts|
|
|
@@ -36,8 +37,8 @@ parser = OptionParser.new do |opts|
|
|
|
36
37
|
)
|
|
37
38
|
|
|
38
39
|
opts.on("-p", "--print FORMAT", "Specify the output format (e.g., 'H' for headers, 'B' for body)") do |format|
|
|
39
|
-
@options[:print] = format
|
|
40
|
-
warn "[Argument Error] Unsupported format: #{format}" unless format.match?(/\A[
|
|
40
|
+
@options[:print] = format || "HB" # default to printing both headers and body
|
|
41
|
+
warn "[Argument Error] Unsupported format: #{format}" unless format.match?(/\A[HBhb]+\z/)
|
|
41
42
|
end
|
|
42
43
|
|
|
43
44
|
opts.on("-a", "--auth USER:PASS", "Specify basic authentication credentials") do |auth|
|
|
@@ -69,9 +70,16 @@ parser = OptionParser.new do |opts|
|
|
|
69
70
|
@options[:output] = file
|
|
70
71
|
end
|
|
71
72
|
|
|
73
|
+
opts.on("--timeout SECONDS", "Set a custom timeout for the request (integer or float)") do |timeout|
|
|
74
|
+
@options[:timeout] = timeout.to_f
|
|
75
|
+
end
|
|
76
|
+
|
|
77
|
+
opts.on("--download", "Download response body to a file specified by --output") do
|
|
78
|
+
@options[:download] = true
|
|
79
|
+
end
|
|
80
|
+
|
|
72
81
|
opts.on("-v", "--verbose", "Print detailed request and response information") do
|
|
73
82
|
@options[:verbose] = true
|
|
74
|
-
|
|
75
83
|
end
|
|
76
84
|
|
|
77
85
|
opts.on("--debug", "Print debug information") do
|
|
@@ -99,81 +107,25 @@ headers = Yobi.args.parse_headers(ARGV)
|
|
|
99
107
|
# prepare authentication header if auth is provided
|
|
100
108
|
Yobi.args.auth_header(headers, @options) if @options[:auth]
|
|
101
109
|
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
# Extend Net::HTTPResponse to add util behaviors
|
|
105
|
-
def offline_mode(request, options)
|
|
106
|
-
Net::HTTP.class_eval do
|
|
107
|
-
def connect; end
|
|
108
|
-
end
|
|
109
|
-
|
|
110
|
-
options[:verbose] = true
|
|
111
|
-
|
|
112
|
-
response = Net::HTTPResponse.new("1.1", "200", "OK")
|
|
113
|
-
response["Content-Type"] = "application/json"
|
|
114
|
-
response["Access-Control-Allow-Credentials"] = true
|
|
115
|
-
response["Access-Control-Allow-Origin"] = "*"
|
|
116
|
-
response["Connection"] = "close"
|
|
117
|
-
response["Date"] = Time.now.httpdate
|
|
118
|
-
response["Server"] = "yobi-offline/#{Yobi::VERSION}"
|
|
119
|
-
response["X-Powered-By"] = "Yobi/#{Yobi::VERSION}"
|
|
120
|
-
response.body = body = JSON.pretty_generate({ message: "Offline mode enabled" })
|
|
121
|
-
|
|
122
|
-
view = Yobi.view(:output)
|
|
123
|
-
puts TTY::Markdown.parse(view.result(binding), color: :always)
|
|
124
|
-
|
|
125
|
-
exit 0
|
|
126
|
-
end
|
|
127
|
-
|
|
128
|
-
def raw_mode(_request, response, options)
|
|
129
|
-
if options[:print].include?("H")
|
|
130
|
-
puts "HTTP/#{response.http_version} #{response.code} #{response.message}"
|
|
131
|
-
response.each_header { |key, value| puts "#{key}: #{value}" }
|
|
132
|
-
puts
|
|
133
|
-
end
|
|
134
|
-
|
|
135
|
-
body = response.parsed_body
|
|
136
|
-
puts body if options[:print].include?("B") && body
|
|
137
|
-
|
|
138
|
-
exit 0
|
|
139
|
-
end
|
|
140
|
-
|
|
141
|
-
def follow_redirects(response, url, method, data, headers, options)
|
|
142
|
-
return response unless response.is_a?(Net::HTTPRedirection)
|
|
143
|
-
|
|
144
|
-
location = response["location"]
|
|
145
|
-
warn "Redirected to #{location}" if options[:debug]
|
|
146
|
-
new_url = URI.join(url, location).to_s
|
|
147
|
-
|
|
148
|
-
Yobi.request(method, new_url, data: data, headers: headers, options: options) do |new_http, new_request|
|
|
149
|
-
response = new_http.request(new_request)
|
|
150
|
-
return follow_redirects(response, new_url, method, data, headers, options)
|
|
151
|
-
end
|
|
152
|
-
end
|
|
153
|
-
|
|
154
|
-
Yobi.request(method, url, data: data, headers: headers, options: @options) do |http, request|
|
|
110
|
+
Yobi::Http.request(method, url, data: data, headers: headers, options: @options) do |http, request|
|
|
155
111
|
options = @options
|
|
156
112
|
|
|
157
|
-
|
|
158
|
-
|
|
113
|
+
# follow redirects, offline, download or standard request mode
|
|
159
114
|
response =
|
|
160
115
|
if options[:follow]
|
|
161
|
-
follow_redirects(http.request(request), url, method, data, headers, options)
|
|
116
|
+
Yobi::Http.follow_redirects(http.request(request), url, method, data, headers, options)
|
|
117
|
+
elsif options[:offline]
|
|
118
|
+
Yobi::Http.offline_mode(request, options)
|
|
119
|
+
elsif options[:download]
|
|
120
|
+
Yobi::Http.download(request, http, options)
|
|
162
121
|
else
|
|
163
122
|
http.request(request)
|
|
164
123
|
end
|
|
165
124
|
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
view = Yobi.view(:output)
|
|
171
|
-
output_result = view.result(binding)
|
|
172
|
-
|
|
173
|
-
if options[:output]
|
|
174
|
-
file = File.expand_path(options[:output], Dir.pwd)
|
|
175
|
-
File.write(file, output_result, mode: "w")
|
|
125
|
+
# render output
|
|
126
|
+
if options[:raw]
|
|
127
|
+
Yobi::Renders::Raw.render(request, response, options)
|
|
176
128
|
else
|
|
177
|
-
|
|
129
|
+
Yobi::Renders::Colored.render(request, response, options)
|
|
178
130
|
end
|
|
179
131
|
end
|
data/lib/yobi/http.rb
CHANGED
|
@@ -8,7 +8,7 @@ module Yobi
|
|
|
8
8
|
METHODS = %w[GET POST PUT DELETE PATCH HEAD OPTIONS].freeze
|
|
9
9
|
|
|
10
10
|
class << self
|
|
11
|
-
# rubocop:disable Metrics/AbcSize
|
|
11
|
+
# rubocop:disable Metrics/AbcSize, Metrics/MethodLength
|
|
12
12
|
def request(method, url, data: {}, headers: {}, options: {})
|
|
13
13
|
@uri = URI(url)
|
|
14
14
|
@options = options
|
|
@@ -18,14 +18,82 @@ module Yobi
|
|
|
18
18
|
request_class = Net::HTTP.const_get(@method)
|
|
19
19
|
request = request_class.new(@uri)
|
|
20
20
|
|
|
21
|
+
if @options[:timeout]
|
|
22
|
+
http.open_timeout = @options[:timeout]
|
|
23
|
+
http.read_timeout = @options[:timeout]
|
|
24
|
+
end
|
|
25
|
+
|
|
21
26
|
headers.each { |key, value| request[key] = value }
|
|
22
27
|
|
|
23
28
|
request.body = data.to_json unless data.empty?
|
|
24
29
|
|
|
25
30
|
yield(http, request) if block_given?
|
|
31
|
+
rescue Net::OpenTimeout, Net::ReadTimeout => e
|
|
32
|
+
warn "Request timed out: #{e.message}"
|
|
33
|
+
exit 1
|
|
34
|
+
end
|
|
35
|
+
end
|
|
36
|
+
# rubocop:enable Metrics/AbcSize, Metrics/MethodLength
|
|
37
|
+
|
|
38
|
+
# rubocop:disable Metrics/ParameterLists
|
|
39
|
+
def follow_redirects(response, url, method, data, headers, options)
|
|
40
|
+
return response unless response.is_a?(Net::HTTPRedirection)
|
|
41
|
+
|
|
42
|
+
location = response["location"]
|
|
43
|
+
warn "Redirected to #{location}" if options[:debug]
|
|
44
|
+
new_url = URI.join(url, location).to_s
|
|
45
|
+
|
|
46
|
+
request(method, new_url, data: data, headers: headers, options: options) do |new_http, new_request|
|
|
47
|
+
response = new_http.request(new_request)
|
|
48
|
+
return follow_redirects(response, new_url, method, data, headers, options)
|
|
26
49
|
end
|
|
27
50
|
end
|
|
28
|
-
# rubocop:enable Metrics/
|
|
51
|
+
# rubocop:enable Metrics/ParameterLists
|
|
52
|
+
|
|
53
|
+
# rubocop:disable Metrics/MethodLength, Metrics/AbcSize
|
|
54
|
+
def offline_mode(_request, options)
|
|
55
|
+
Net::HTTP.class_eval do
|
|
56
|
+
def connect; end
|
|
57
|
+
end
|
|
58
|
+
|
|
59
|
+
options[:verbose] = true
|
|
60
|
+
|
|
61
|
+
Net::HTTPResponse.new("1.1", "200", "OK").tap do |response|
|
|
62
|
+
response["Content-Type"] = "application/json"
|
|
63
|
+
response["Access-Control-Allow-Credentials"] = true
|
|
64
|
+
response["Access-Control-Allow-Origin"] = "*"
|
|
65
|
+
response["Connection"] = "close"
|
|
66
|
+
response["Date"] = Time.now.httpdate
|
|
67
|
+
response["Server"] = "yobi-offline/#{Yobi::VERSION}"
|
|
68
|
+
response["X-Powered-By"] = "Yobi/#{Yobi::VERSION}"
|
|
69
|
+
|
|
70
|
+
response.body = JSON.pretty_generate({ message: "Offline mode enabled" })
|
|
71
|
+
response.instance_variable_set(:@read, true)
|
|
72
|
+
end
|
|
73
|
+
end
|
|
74
|
+
# rubocop:enable Metrics/MethodLength, Metrics/AbcSize
|
|
75
|
+
|
|
76
|
+
# rubocop:disable Metrics/MethodLength, Metrics/AbcSize
|
|
77
|
+
def download(request, http, options)
|
|
78
|
+
http.request(request) do |response|
|
|
79
|
+
url = request.uri.to_s
|
|
80
|
+
total_bytes = response["Content-Length"]&.to_i
|
|
81
|
+
progress = Yobi::UI::Progress.new(total_bytes)
|
|
82
|
+
|
|
83
|
+
filename = options[:output] || File.basename(URI.parse(url).path)
|
|
84
|
+
File.open(filename, "wb") do |file|
|
|
85
|
+
response.read_body do |chunk|
|
|
86
|
+
file.write(chunk)
|
|
87
|
+
progress.increment(chunk.size)
|
|
88
|
+
end
|
|
89
|
+
end
|
|
90
|
+
|
|
91
|
+
puts "\nDownload finished: #{filename}"
|
|
92
|
+
end
|
|
93
|
+
|
|
94
|
+
exit 0
|
|
95
|
+
end
|
|
96
|
+
# rubocop:enable Metrics/MethodLength, Metrics/AbcSize
|
|
29
97
|
end
|
|
30
98
|
end
|
|
31
99
|
end
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Yobi
|
|
4
|
+
module Renders
|
|
5
|
+
# Render a colored output of the request and response
|
|
6
|
+
module Colored
|
|
7
|
+
class << self
|
|
8
|
+
def compile(request, response, options = {})
|
|
9
|
+
body =
|
|
10
|
+
JSON.pretty_generate(response.parsed_body) rescue response.body # rubocop:disable Style/RescueModifier
|
|
11
|
+
|
|
12
|
+
view = Yobi.view(:output)
|
|
13
|
+
TTY::Markdown.parse(view.result(binding), color: :always)
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
def render(request, response, options = {})
|
|
17
|
+
view = compile(request, response, options)
|
|
18
|
+
|
|
19
|
+
if options[:output]
|
|
20
|
+
file_path = ::File.expand_path(options[:output], Dir.pwd)
|
|
21
|
+
::File.write(file_path, view, mode: "wb")
|
|
22
|
+
else
|
|
23
|
+
puts view
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
exit 0
|
|
27
|
+
end
|
|
28
|
+
end
|
|
29
|
+
end
|
|
30
|
+
end
|
|
31
|
+
end
|
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Yobi
|
|
4
|
+
module Renders
|
|
5
|
+
# Render a raw output of the request and response
|
|
6
|
+
module Raw
|
|
7
|
+
class << self
|
|
8
|
+
def compile(request, response, options = {})
|
|
9
|
+
arguments request: request, response: response, options: options
|
|
10
|
+
|
|
11
|
+
buffer.tap do
|
|
12
|
+
compile_request
|
|
13
|
+
compile_response
|
|
14
|
+
end
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
def render(request, response, options = {})
|
|
18
|
+
view = compile(request, response, options)
|
|
19
|
+
|
|
20
|
+
if options[:output]
|
|
21
|
+
file_path = ::File.expand_path(options[:output], Dir.pwd)
|
|
22
|
+
::File.write(file_path, view, mode: "wb")
|
|
23
|
+
else
|
|
24
|
+
puts view
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
exit 0
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
private
|
|
31
|
+
|
|
32
|
+
attr_reader :request, :response, :options
|
|
33
|
+
|
|
34
|
+
def buffer
|
|
35
|
+
@buffer ||= String.new
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
def arguments(**args)
|
|
39
|
+
@request = args[:request]
|
|
40
|
+
@response = args[:response]
|
|
41
|
+
@options = args[:options] || {}
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
def show_header?(request_or_response)
|
|
45
|
+
options[:print].include?(request_or_response.is_a?(Net::HTTPResponse) ? "H" : "h")
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
def show_body?(request_or_response)
|
|
49
|
+
options[:print].include?(request_or_response.is_a?(Net::HTTPResponse) ? "B" : "b")
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
def compile_request
|
|
53
|
+
compile_request_header
|
|
54
|
+
compile_request_body
|
|
55
|
+
|
|
56
|
+
buffer << "\n" * 2 if show_header?(request) || show_body?(request)
|
|
57
|
+
end
|
|
58
|
+
|
|
59
|
+
def compile_request_header
|
|
60
|
+
return unless show_header? request
|
|
61
|
+
|
|
62
|
+
buffer << "#{request.method} #{request.path} HTTP/1.1 \n"
|
|
63
|
+
request.each_capitalized.sort.each do |key, value|
|
|
64
|
+
buffer << "#{key}: #{value}\n"
|
|
65
|
+
end
|
|
66
|
+
buffer << "\n"
|
|
67
|
+
end
|
|
68
|
+
|
|
69
|
+
def compile_request_body
|
|
70
|
+
return unless show_body?(request) && request.body && !request.body.empty?
|
|
71
|
+
|
|
72
|
+
buffer << "#{JSON.pretty_generate(request.parsed_body) rescue request.body}\n" # rubocop:disable Style/RescueModifier
|
|
73
|
+
end
|
|
74
|
+
|
|
75
|
+
def compile_response
|
|
76
|
+
compile_response_header
|
|
77
|
+
compile_response_body
|
|
78
|
+
end
|
|
79
|
+
|
|
80
|
+
def compile_response_header
|
|
81
|
+
return unless show_header? response
|
|
82
|
+
|
|
83
|
+
buffer << "HTTP/#{response.http_version} #{response.code} #{response.message}\n"
|
|
84
|
+
response.each_header { |key, value| buffer << "#{key}: #{value}\n" }
|
|
85
|
+
buffer << "\n"
|
|
86
|
+
end
|
|
87
|
+
|
|
88
|
+
def compile_response_body
|
|
89
|
+
return unless response.body && !response.body.empty? && options[:print].include?("B")
|
|
90
|
+
|
|
91
|
+
buffer << "#{JSON.pretty_generate(response.parsed_body)}\n"
|
|
92
|
+
end
|
|
93
|
+
end
|
|
94
|
+
end
|
|
95
|
+
end
|
|
96
|
+
end
|
data/lib/yobi/renders.rb
ADDED
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Yobi
|
|
4
|
+
module UI
|
|
5
|
+
# Simple terminal progress bar
|
|
6
|
+
class Progress
|
|
7
|
+
BAR_WIDTH = 40
|
|
8
|
+
|
|
9
|
+
STYLE = {
|
|
10
|
+
arrow: { complete: "▸", incomplete: "▹", unknown: "◂▸" },
|
|
11
|
+
asterisk: { complete: "✱", incomplete: "✳", unknown: "✳✱✳" },
|
|
12
|
+
blade: { complete: "▰", incomplete: "▱", unknown: "▱▰▱" },
|
|
13
|
+
block: { complete: "█", incomplete: "░", unknown: "░█░" },
|
|
14
|
+
box: { complete: "■", incomplete: "□", unknown: "□■□" },
|
|
15
|
+
bracket: { complete: "❭", incomplete: " ", unknown: "❬=❭" },
|
|
16
|
+
burger: { complete: "≡", incomplete: " ", unknown: "<≡>" },
|
|
17
|
+
button: { complete: "⦿", incomplete: "⦾", unknown: "⦾⦿⦾" },
|
|
18
|
+
chevron: { complete: "›", incomplete: " ", unknown: "‹=›" },
|
|
19
|
+
circle: { complete: "●", incomplete: "○", unknown: "○●○" },
|
|
20
|
+
classic: { complete: "=", incomplete: " ", unknown: "<=>" },
|
|
21
|
+
crate: { complete: "▣", incomplete: "⬚", unknown: "⬚▣⬚" },
|
|
22
|
+
diamond: { complete: "♦", incomplete: "♢", unknown: "♢♦♢" },
|
|
23
|
+
dot: { complete: "・", incomplete: " ", unknown: "・・・" },
|
|
24
|
+
heart: { complete: "♥", incomplete: "♡", unknown: "♡♥♡" },
|
|
25
|
+
star: { complete: "★", incomplete: "☆", unknown: "☆★☆" }
|
|
26
|
+
}.freeze
|
|
27
|
+
|
|
28
|
+
def initialize(total_bytes = nil, options = { style: :block })
|
|
29
|
+
@total = total_bytes
|
|
30
|
+
@downloaded = 0
|
|
31
|
+
@last_draw = Time.now
|
|
32
|
+
@options = options
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
def increment(bytes)
|
|
36
|
+
@downloaded += bytes
|
|
37
|
+
current_time = Time.now
|
|
38
|
+
|
|
39
|
+
return unless current_time - @last_draw >= 0.1 || finished?
|
|
40
|
+
|
|
41
|
+
draw
|
|
42
|
+
@last_draw = current_time
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
def finished?
|
|
46
|
+
@total && @downloaded >= @total
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
def draw
|
|
50
|
+
if @total
|
|
51
|
+
percent = @downloaded.to_f / @total
|
|
52
|
+
filled = (percent * BAR_WIDTH).round
|
|
53
|
+
bar = theme[:complete] * filled + theme[:incomplete] * (BAR_WIDTH - filled)
|
|
54
|
+
print "\r[#{bar}] #{(percent * 100).round(1)}% "\
|
|
55
|
+
"(#{human(@downloaded)}/#{human(@total)})"
|
|
56
|
+
else
|
|
57
|
+
print "\r#{human(@downloaded)} downloaded"
|
|
58
|
+
end
|
|
59
|
+
|
|
60
|
+
$stdout.flush
|
|
61
|
+
end
|
|
62
|
+
|
|
63
|
+
private
|
|
64
|
+
|
|
65
|
+
def theme
|
|
66
|
+
STYLE[@options[:style]] || STYLE[:box]
|
|
67
|
+
end
|
|
68
|
+
|
|
69
|
+
def human(bytes)
|
|
70
|
+
if bytes > 1_048_576
|
|
71
|
+
format("%.2f MiB", bytes / 1_048_576.0)
|
|
72
|
+
elsif bytes > 1024
|
|
73
|
+
format("%.2f KiB", bytes / 1024.0)
|
|
74
|
+
else
|
|
75
|
+
"#{bytes} B"
|
|
76
|
+
end
|
|
77
|
+
end
|
|
78
|
+
end
|
|
79
|
+
end
|
|
80
|
+
end
|
data/lib/yobi/ui.rb
ADDED
data/lib/yobi/version.rb
CHANGED
data/lib/yobi.rb
CHANGED
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.
|
|
4
|
+
version: 0.12.1
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Daniel Vinciguerra
|
|
8
8
|
bindir: exe
|
|
9
9
|
cert_chain: []
|
|
10
|
-
date: 2026-02-
|
|
10
|
+
date: 2026-02-24 00:00:00.000000000 Z
|
|
11
11
|
dependencies:
|
|
12
12
|
- !ruby/object:Gem::Dependency
|
|
13
13
|
name: base64
|
|
@@ -37,8 +37,18 @@ dependencies:
|
|
|
37
37
|
- - "~>"
|
|
38
38
|
- !ruby/object:Gem::Version
|
|
39
39
|
version: '0.7'
|
|
40
|
-
description:
|
|
41
|
-
|
|
40
|
+
description: |
|
|
41
|
+
Yobi is a terminal tool to make HTTP requests and display responses in a friendly way inspired by HTTPie.
|
|
42
|
+
|
|
43
|
+
It allows you to easily send HTTP requests and view the responses in a human-readable format, making it easier
|
|
44
|
+
to debug and test APIs from the command line.
|
|
45
|
+
|
|
46
|
+
The main features of Yobi include:
|
|
47
|
+
* Support for various HTTP methods (GET, POST, PUT, DELETE, etc.)
|
|
48
|
+
* Customizable request headers and body
|
|
49
|
+
* Pretty-printed responses with syntax highlighting
|
|
50
|
+
* Download response content to a file
|
|
51
|
+
* Low dependency and easy installation
|
|
42
52
|
email:
|
|
43
53
|
- daniel.vinciguerra@bivee.com.br
|
|
44
54
|
executables:
|
|
@@ -50,6 +60,8 @@ files:
|
|
|
50
60
|
- Agents.md
|
|
51
61
|
- CHANGELOG.md
|
|
52
62
|
- CODE_OF_CONDUCT.md
|
|
63
|
+
- Examples.md
|
|
64
|
+
- MIT-LICENSE
|
|
53
65
|
- README.md
|
|
54
66
|
- Rakefile
|
|
55
67
|
- exe/yobi
|
|
@@ -61,6 +73,11 @@ files:
|
|
|
61
73
|
- lib/yobi/extensions/net_http.rb
|
|
62
74
|
- lib/yobi/extensions/net_http/parsing.rb
|
|
63
75
|
- lib/yobi/http.rb
|
|
76
|
+
- lib/yobi/renders.rb
|
|
77
|
+
- lib/yobi/renders/colored.rb
|
|
78
|
+
- lib/yobi/renders/raw.rb
|
|
79
|
+
- lib/yobi/ui.rb
|
|
80
|
+
- lib/yobi/ui/progress.rb
|
|
64
81
|
- lib/yobi/version.rb
|
|
65
82
|
- screenshot.png
|
|
66
83
|
- spec/extensions/net_http/parsing_spec.rb
|
|
@@ -70,7 +87,8 @@ files:
|
|
|
70
87
|
- spec/yobi/http_spec.rb
|
|
71
88
|
- spec/yobi_spec.rb
|
|
72
89
|
homepage: https://github.com/dvinciguerra/yobi-http
|
|
73
|
-
licenses:
|
|
90
|
+
licenses:
|
|
91
|
+
- MIT
|
|
74
92
|
metadata:
|
|
75
93
|
homepage_uri: https://github.com/dvinciguerra/yobi-http
|
|
76
94
|
source_code_uri: https://github.com/dvinciguerra/yobi-http
|
|
@@ -91,6 +109,6 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
|
91
109
|
requirements: []
|
|
92
110
|
rubygems_version: 3.6.2
|
|
93
111
|
specification_version: 4
|
|
94
|
-
summary: Yobi is a
|
|
95
|
-
|
|
112
|
+
summary: Yobi is a terminal tool to make HTTP requests and display responses in a
|
|
113
|
+
friendly way inspired by HTTPie.
|
|
96
114
|
test_files: []
|