fetch-api 0.1.0 → 0.2.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: d8968e4c64a4d0b5370aef72a78f1b4ac04096057deb92d1bc1a6dd14353b3cd
4
- data.tar.gz: 2b7cdf82b75cf2c2d51abd5693f1ad278242e4e93645779b0909bfa7fd62de7a
3
+ metadata.gz: ae66c4ee1f7d2b11e55480e6456be9eb78f5b1f6a2da0a22fb0cef1b4388328b
4
+ data.tar.gz: 2995e13999494f718966a8dc3f293342593a17f88a0c003b702243ae710492e2
5
5
  SHA512:
6
- metadata.gz: 39bfda51fed25d97acb41128134615980b4eb35357a790d76c79feb1bf57c06e7f2047e2908c530d346e1b051f79b1846cf01c912209d1be6cebb7d1eeaf88c1
7
- data.tar.gz: 42cd5a712cfe067f16e70482c6013162d2a29d76666d709d5032badc79a4202a9c474d1ba38dd19598657ae1b2b0d51f5de32ef49ce7395d6221ba1f7d752526
6
+ metadata.gz: 81683853c7d4e62b19dd0e4472b343958d656c361251239e160c44504b5671ba3b32078626433fd63f0be5a5a6290564cdc232f1db0a2c8dbbf462320b2102d8
7
+ data.tar.gz: ca6a9131aa1a4a3c504e74521b3c8244e89a66cc4f5e2e9bb27a7974352c910a7a869cd1ab96b531ccf694f3565e5ba93ae8de21d374006ac533ddb0f289241e
data/README.md CHANGED
@@ -1,4 +1,4 @@
1
- # Fetch API
1
+ # Fetch API for Ruby
2
2
 
3
3
  Ruby's Net::HTTP is very powerful, but has a complicated API. OpenURI is easy to use, but has limited functionality. Third-party HTTP clients each have different APIs, and it can sometimes be difficult to learn how to use them.
4
4
 
@@ -30,9 +30,11 @@ require 'fetch-api'
30
30
  res = Fetch::API.fetch('https://example.com')
31
31
 
32
32
  # res is a Rack::Response object
33
- puts res.body
33
+ puts res.body.first
34
34
  ```
35
35
 
36
+ or
37
+
36
38
  ``` ruby
37
39
  require 'fetch-api'
38
40
 
@@ -41,6 +43,13 @@ include Fetch::API
41
43
  res = fetch('https://example.com')
42
44
  ```
43
45
 
46
+ Supported options are as follows:
47
+
48
+ - `method`: HTTP method (default: `'GET'`)
49
+ - `headers`: Request headers (default: `{}`)
50
+ - `body`: Request body (default: `nil`)
51
+ - `redirect`: Follow redirects (one of `follow`, `error`, `manual`, default: `follow`)
52
+
44
53
  ### Post JSON
45
54
 
46
55
  ``` ruby
@@ -57,23 +66,30 @@ res = fetch('http://example.com', **{
57
66
  })
58
67
  ```
59
68
 
60
- ### Post Form
69
+ ### Post application/x-www-form-urlencoded
61
70
 
62
71
  ``` ruby
63
72
  res = fetch('http://example.com', **{
64
73
  method: 'POST',
65
74
 
66
- headers: {
67
- 'Content-Type' => 'multipart/form-data'
68
- },
69
-
70
- body: Rack::Multipart.build_multipart(
71
- file: Rack::Multipart::UploadedFile.new(io: StringIO.new('foo'), filename: 'foo.txt')
75
+ body: Fetch::URLSearchParams.new(
76
+ name: 'Alice'
72
77
  )
73
78
  })
74
79
  ```
75
80
 
76
- Note: `Rack::Multipart.build_multipart` returns nil if the parameter does not include UploadedFile.
81
+ ### Post multipart/form-data
82
+
83
+ ``` ruby
84
+ res = fetch('http://example.com', **{
85
+ method: 'POST',
86
+
87
+ body: Fetch::FormData.build(
88
+ name: 'Alice',
89
+ file: File.open('path/to/file.txt')
90
+ )
91
+ })
92
+ ```
77
93
 
78
94
  ## Development
79
95
 
data/lib/fetch/client.rb CHANGED
@@ -1,7 +1,9 @@
1
1
  require_relative '../fetch'
2
+ require_relative 'form_data'
3
+ require_relative 'url_search_params'
2
4
 
5
+ require 'marcel'
3
6
  require 'net/http'
4
- require 'net/https'
5
7
  require 'rack/response'
6
8
  require 'singleton'
7
9
  require 'uri'
@@ -18,7 +20,25 @@ module Fetch
18
20
  req[k] = v
19
21
  end
20
22
 
21
- req.body = body
23
+ case body
24
+ when FormData
25
+ req.set_form body.entries.map {|k, v|
26
+ if v.is_a?(File)
27
+ [k, v, {
28
+ filename: File.basename(v.path),
29
+ content_type: Marcel::MimeType.for(v) || 'application/octet-stream'
30
+ }]
31
+ else
32
+ [k, v]
33
+ end
34
+ }, 'multipart/form-data'
35
+ when URLSearchParams
36
+ req['Content-Type'] ||= 'application/x-www-form-urlencoded'
37
+
38
+ req.body = body.to_s
39
+ else
40
+ req.body = body
41
+ end
22
42
 
23
43
  http = Net::HTTP.new(uri.hostname, uri.port)
24
44
  http.use_ssl = uri.scheme == 'https'
@@ -29,9 +49,9 @@ module Fetch
29
49
  when Net::HTTPRedirection
30
50
  case redirect
31
51
  when 'follow'
32
- fetch(res['location'], method:, headers:, body:, redirect:)
52
+ fetch(res['Location'], method:, headers:, body:, redirect:)
33
53
  when 'error'
34
- raise RedirectError, "redirected to #{res['location']}"
54
+ raise RedirectError, "redirected to #{res['Location']}"
35
55
  when 'manual'
36
56
  to_rack_response(res)
37
57
  else
@@ -0,0 +1,60 @@
1
+ module Fetch
2
+ class FormData
3
+ include Enumerable
4
+
5
+ def self.build(enumerable)
6
+ data = FormData.new
7
+
8
+ enumerable.each do |k, v|
9
+ data.append k, v
10
+ end
11
+
12
+ data
13
+ end
14
+
15
+ def initialize
16
+ @entries = []
17
+ end
18
+
19
+ attr_reader :entries
20
+
21
+ def append(key, value)
22
+ @entries.push [key.to_s, value]
23
+ end
24
+
25
+ def delete(key)
26
+ @entries.reject! {|k,| k == key.to_s }
27
+ end
28
+
29
+ def get(key)
30
+ @entries.assoc(key.to_s)&.last
31
+ end
32
+
33
+ def get_all(key)
34
+ @entries.select {|k,| k == key.to_s }.map(&:last)
35
+ end
36
+
37
+ def has(key)
38
+ @entries.any? {|k,| k == key.to_s }
39
+ end
40
+
41
+ def keys
42
+ @entries.map(&:first)
43
+ end
44
+
45
+ def set(key, value)
46
+ delete key
47
+ append key, value
48
+ end
49
+
50
+ def values
51
+ @entries.map(&:last)
52
+ end
53
+
54
+ def each(&block)
55
+ return enum_for(:each) unless block_given?
56
+
57
+ @entries.each(&block)
58
+ end
59
+ end
60
+ end
@@ -0,0 +1,71 @@
1
+ require 'uri'
2
+
3
+ module Fetch
4
+ class URLSearchParams
5
+ include Enumerable
6
+
7
+ def initialize(options = [])
8
+ @entries = []
9
+
10
+ case options
11
+ when String
12
+ initialize(URI.decode_www_form(options.delete_prefix('?')))
13
+ when Enumerable
14
+ options.each do |k, v|
15
+ append k, v
16
+ end
17
+ else
18
+ raise ArgumentError
19
+ end
20
+ end
21
+
22
+ attr_reader :entries
23
+
24
+ def append(key, value)
25
+ @entries.push [key.to_s, value]
26
+ end
27
+
28
+ def delete(key)
29
+ @entries.reject! {|k,| k == key.to_s }
30
+ end
31
+
32
+ def get(key)
33
+ @entries.assoc(key.to_s)&.last
34
+ end
35
+
36
+ def get_all(key)
37
+ @entries.select {|k,| k == key.to_s }.map(&:last)
38
+ end
39
+
40
+ def has(key)
41
+ @entries.any? {|k,| k == key.to_s }
42
+ end
43
+
44
+ def keys
45
+ @entries.map(&:first)
46
+ end
47
+
48
+ def set(key, value)
49
+ delete key
50
+ append key, value
51
+ end
52
+
53
+ def sort
54
+ @entries.sort_by!(&:first)
55
+ end
56
+
57
+ def to_s
58
+ URI.encode_www_form(@entries)
59
+ end
60
+
61
+ def values
62
+ @entries.map(&:last)
63
+ end
64
+
65
+ def each(&block)
66
+ return enum_for(:each) unless block_given?
67
+
68
+ @entries.each(&block)
69
+ end
70
+ end
71
+ end
data/lib/fetch/version.rb CHANGED
@@ -1,3 +1,3 @@
1
1
  module Fetch
2
- VERSION = '0.1.0'
2
+ VERSION = '0.2.0'
3
3
  end
data/lib/fetch.rb CHANGED
@@ -1,5 +1,4 @@
1
1
  require_relative 'fetch/version'
2
- require_relative 'fetch/api'
3
2
 
4
3
  module Fetch
5
4
  class Error < StandardError; end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: fetch-api
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.0
4
+ version: 0.2.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Keita Urashima
@@ -10,6 +10,20 @@ bindir: exe
10
10
  cert_chain: []
11
11
  date: 2024-07-06 00:00:00.000000000 Z
12
12
  dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: marcel
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - ">="
18
+ - !ruby/object:Gem::Version
19
+ version: '0'
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - ">="
25
+ - !ruby/object:Gem::Version
26
+ version: '0'
13
27
  - !ruby/object:Gem::Dependency
14
28
  name: net-http
15
29
  requirement: !ruby/object:Gem::Requirement
@@ -79,6 +93,8 @@ files:
79
93
  - lib/fetch.rb
80
94
  - lib/fetch/api.rb
81
95
  - lib/fetch/client.rb
96
+ - lib/fetch/form_data.rb
97
+ - lib/fetch/url_search_params.rb
82
98
  - lib/fetch/version.rb
83
99
  - sig/fetch.rbs
84
100
  homepage: https://github.com/ursm/fetch-api