yobi-http 0.14.0 → 0.18.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: 42bdd81cec29bd949acd4b1a96698e219bc6298a71eced3aa36c8cee4b965dc4
4
- data.tar.gz: f0cf545e7c5d13f5543dbbfa63e72d563d03afc12d74cdbd45c47479f14064a6
3
+ metadata.gz: b5994458b4f28cc4879858782a1dbc26a90f8ab77d7bf97b5a13ee7aaec13092
4
+ data.tar.gz: ac095c048327a1ce8260f388dcc414c647fa9f912f15ab9f43fabb1327225f69
5
5
  SHA512:
6
- metadata.gz: bf2635eba8c559e6459d638dd944d315cb571833d60cffa208a7629d4d07110c86a39ac924b18639e3e94381575ea2cdbf039c70e9186006f69f11f889a8ae30
7
- data.tar.gz: 98f048dbbf3512cb34782e14d26f737a06ea6ce68ca75ab2df13b158d5a8b8f00e9d7a0f7d08b0518254559ca587ee6bc5114bed543b61522816387ccab9a8be
6
+ metadata.gz: 9a1db1c25476849b0169d3d535d3ced35f2d14034363617211aff38f7903970d9056269231d83e8617392c66587eae32ef42fd573080bd3f5165d8b7de63f63f
7
+ data.tar.gz: 2ba909e076ccf9a12ff9f624ebe545675d6c540aaa248951a0ff7a5504a38d476c38b07e1fe321e5de921fd693a01be84dcee6169f6a23cde0ce7e01c3020c95
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
19
+ timeout: nil, download: false, output: nil, content_type: :json
20
20
  }
21
21
 
22
22
  parser = OptionParser.new do |opts|
@@ -28,7 +28,7 @@ parser = OptionParser.new do |opts|
28
28
 
29
29
  Examples:
30
30
  yobi https://api.example.com/users
31
- yobi POST https://api.example.com/users name=John age=30
31
+ yobi POST https://api.example.com/products name="New Product" price:=19.99 enabled:=true tags:=@tags.json
32
32
  yobi GET :8080/api/items Authorization:"Bearer $TOKEN"
33
33
  yobi -A basic -a user:pass https://api.example.com/secure-data
34
34
  yobi --raw --print B POST https://httpbin.org/anything | jq '.headers["User-Agent"]'
@@ -53,9 +53,40 @@ parser = OptionParser.new do |opts|
53
53
  @options[:raw] = true
54
54
  end
55
55
 
56
- opts.on("-h", "--help", "Print this help message") do
57
- puts opts
58
- exit
56
+ opts.on("-h", "--headers", "Print only response headers") do
57
+ @options[:raw] = true
58
+ @options[:print] = "H"
59
+ end
60
+
61
+ opts.on("-b", "--body", "Print only response body") do
62
+ @options[:raw] = true
63
+ @options[:print] = "B"
64
+ end
65
+
66
+ opts.on("--status", "Print only response status code") do
67
+ @options[:raw] = true
68
+ @options[:print] = "status"
69
+ end
70
+
71
+ opts.on("--form", "Send data as application/x-www-form-urlencoded instead of JSON") do
72
+ @options[:content_type] = :form
73
+ end
74
+
75
+ opts.on("--multipart", "Send data as multipart/form-data instead of JSON") do
76
+ @options[:content_type] = :multipart
77
+ end
78
+
79
+ opts.on("--xml", "Send data as application/xml instead of JSON") do
80
+ @options[:content_type] = :xml
81
+ end
82
+
83
+ opts.on("--binary", "Send data as application/octet-stream instead of JSON") do
84
+ @options[:content_type] = :binary
85
+ end
86
+
87
+ opts.on("--json", "Send data as application/json (default)") do
88
+ # This option is implicit since JSON is the default, but we can set it explicitly if needed
89
+ @options[:content_type] = :json
59
90
  end
60
91
 
61
92
  opts.on("--offline", "Run in offline mode (prepare request but do not send)") do
@@ -91,6 +122,11 @@ parser = OptionParser.new do |opts|
91
122
  puts "#{Yobi.name} v#{Yobi::VERSION}"
92
123
  exit
93
124
  end
125
+
126
+ opts.on("--help", "Print this help message") do
127
+ puts opts
128
+ exit
129
+ end
94
130
  end
95
131
 
96
132
  parser.parse!(ARGV)
@@ -101,13 +137,14 @@ method = Yobi.args.http_method?(ARGV[0]) ? ARGV.shift : "GET"
101
137
  # resolve the URL
102
138
  url = Yobi.args.url(ARGV.shift)
103
139
 
104
- data = Yobi.args.parse_data(ARGV)
105
- headers = Yobi.args.parse_headers(ARGV)
140
+ data = Yobi.args.parse_data(ARGV, @options)
141
+ query = Yobi.args.parse_query(ARGV, @options)
142
+ headers = Yobi.args.parse_headers(ARGV, @options)
106
143
 
107
144
  # prepare authentication header if auth is provided
108
145
  Yobi.args.auth_header(headers, @options) if @options[:auth]
109
146
 
110
- Yobi::Http.request(method, url, data: data, headers: headers, options: @options) do |http, request|
147
+ Yobi::Http.request(method, url, data: data, headers: headers, query: query, options: @options) do |http, request|
111
148
  options = @options
112
149
 
113
150
  # follow redirects, offline, download or standard request mode
@@ -123,7 +160,9 @@ Yobi::Http.request(method, url, data: data, headers: headers, options: @options)
123
160
  end
124
161
 
125
162
  # render output
126
- if options[:raw]
163
+ if options[:raw] && options[:print].downcase == "status"
164
+ print response.code
165
+ elsif options[:raw]
127
166
  Yobi::Renders::Raw.render(request, response, options)
128
167
  else
129
168
  Yobi::Renders::Colored.render(request, response, options)
@@ -22,12 +22,16 @@ module Yobi
22
22
  end
23
23
  end
24
24
 
25
+ def parse_query(args, _options)
26
+ args.select { |arg| arg.match?(/::/) }.map.to_h { |arg| arg.split("::", 2).map(&:strip) }
27
+ end
28
+
25
29
  # rubocop:disable Metrics/AbcSize, Metrics/MethodLength, Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity
26
- def parse_data(args)
27
- args.select { |arg| arg.match?("^.*(=|:=|=@|:=@).*$") }.map.to_h do |arg|
30
+ def parse_data(args, _options)
31
+ args.select { |arg| arg.match?("^.*(={1}|:=|=@|:=@).*$") }.map do |arg|
28
32
  case arg
29
33
  when /:=@/
30
- arg.split(/:=@/, 2).map do |part|
34
+ arg.split(":=@", 2).map do |part|
31
35
  part = String(part)&.strip
32
36
  File.exist?(part) ? JSON.parse(File.read(part)) : part
33
37
  end
@@ -47,15 +51,18 @@ module Yobi
47
51
  else
48
52
  arg.split("=", 2).map(&:strip)
49
53
  end
50
- end
54
+ end.compact.to_h
51
55
  end
52
56
  # rubocop:enable Metrics/AbcSize, Metrics/MethodLength, Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity
53
57
 
54
- def parse_headers(args)
55
- { "Content-Type" => "application/json", "User-Agent" => "#{Yobi.name}/#{Yobi::VERSION}" }
56
- .merge(args.select do |arg|
57
- arg.match?(/:/) && !arg.match?(/:=/)
58
- end.map.to_h { |arg| arg.split(":", 2).map(&:strip) })
58
+ def parse_headers(args, options)
59
+ content_type = content_type_for options[:content_type]
60
+
61
+ { "Content-Type" => content_type, "User-Agent" => "#{Yobi.name}/#{Yobi::VERSION}" }.merge(
62
+ args
63
+ .select { |arg| arg.match?(/:{1}/) && !arg.match?(/:=/) && !arg.match?(/::/) }
64
+ .map.to_h { |arg| arg.split(/:{1}/, 2).map(&:strip) }
65
+ )
59
66
  end
60
67
 
61
68
  def auth_header(headers, options)
@@ -73,6 +80,25 @@ module Yobi
73
80
  exit 1
74
81
  end
75
82
  end
83
+
84
+ private
85
+
86
+ def content_type_for(value)
87
+ case value
88
+ when :form
89
+ "application/x-www-form-urlencoded"
90
+ when :multipart
91
+ "multipart/form-data"
92
+ when :xml
93
+ "application/xml"
94
+ when :binary
95
+ "application/octet-stream"
96
+ when :json, nil
97
+ "application/json"
98
+ else
99
+ "application/json"
100
+ end
101
+ end
76
102
  end
77
103
  end
78
104
  end
data/lib/yobi/http.rb CHANGED
@@ -1,5 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require "json"
3
4
  require "net/http"
4
5
 
5
6
  module Yobi
@@ -7,10 +8,22 @@ module Yobi
7
8
  module Http
8
9
  METHODS = %w[GET POST PUT DELETE PATCH HEAD OPTIONS].freeze
9
10
 
11
+ CONTENT_TYPES = {
12
+ text: "text/plain",
13
+ html: "text/html",
14
+ form: "application/x-www-form-urlencoded",
15
+ multipart: "multipart/form-data",
16
+ xml: "application/xml",
17
+ binary: "application/octet-stream",
18
+ json: "application/json"
19
+ }.freeze
20
+
10
21
  class << self
11
- # rubocop:disable Metrics/AbcSize, Metrics/MethodLength
12
- def request(method, url, data: {}, headers: {}, options: {})
22
+ # rubocop:disable Metrics/AbcSize, Metrics/MethodLength, Metrics/ParameterLists
23
+ def request(method, url, data: {}, query: {}, headers: {}, options: {})
13
24
  @uri = URI(url)
25
+ @uri.query = URI.encode_www_form(**query) unless query.empty?
26
+
14
27
  @options = options
15
28
  @method = method.capitalize
16
29
 
@@ -28,12 +41,18 @@ module Yobi
28
41
  request.body = data.to_json unless data.empty?
29
42
 
30
43
  yield(http, request) if block_given?
31
- rescue Net::OpenTimeout, Net::ReadTimeout => e
32
- warn "Request timed out: #{e.message}"
33
- exit 1
34
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
35
54
  end
36
- # rubocop:enable Metrics/AbcSize, Metrics/MethodLength
55
+ # rubocop:enable Metrics/AbcSize, Metrics/MethodLength, Metrics/ParameterLists
37
56
 
38
57
  # rubocop:disable Metrics/ParameterLists
39
58
  def follow_redirects(response, url, method, data, headers, options)
@@ -50,7 +69,7 @@ module Yobi
50
69
  end
51
70
  # rubocop:enable Metrics/ParameterLists
52
71
 
53
- # rubocop:disable Metrics/MethodLength, Metrics/AbcSize
72
+ # rubocop:disable Metrics/AbcSize
54
73
  def offline_mode(_request, options)
55
74
  Net::HTTP.class_eval do
56
75
  def connect; end
@@ -59,7 +78,7 @@ module Yobi
59
78
  options[:verbose] = true
60
79
 
61
80
  Net::HTTPResponse.new("1.1", "200", "OK").tap do |response|
62
- response["Content-Type"] = "application/json"
81
+ response["Content-Type"] = CONTENT_TYPES.fetch(options[:content_type], CONTENT_TYPES[:json])
63
82
  response["Access-Control-Allow-Credentials"] = true
64
83
  response["Access-Control-Allow-Origin"] = "*"
65
84
  response["Connection"] = "close"
@@ -71,9 +90,9 @@ module Yobi
71
90
  response.instance_variable_set(:@read, true)
72
91
  end
73
92
  end
74
- # rubocop:enable Metrics/MethodLength, Metrics/AbcSize
93
+ # rubocop:enable Metrics/AbcSize
75
94
 
76
- # rubocop:disable Metrics/MethodLength, Metrics/AbcSize
95
+ # rubocop:disable Metrics/AbcSize
77
96
  def download(request, http, options)
78
97
  http.request(request) do |response|
79
98
  url = request.uri.to_s
@@ -93,7 +112,7 @@ module Yobi
93
112
 
94
113
  exit 0
95
114
  end
96
- # rubocop:enable Metrics/MethodLength, Metrics/AbcSize
115
+ # rubocop:enable Metrics/AbcSize
97
116
  end
98
117
  end
99
118
  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.14.0"
5
+ VERSION = "0.18.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.14.0
4
+ version: 0.18.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Daniel Vinciguerra
8
8
  bindir: exe
9
9
  cert_chain: []
10
- date: 2026-02-27 00:00:00.000000000 Z
10
+ date: 2026-03-05 00:00:00.000000000 Z
11
11
  dependencies:
12
12
  - !ruby/object:Gem::Dependency
13
13
  name: base64