relax2 0.1.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 +7 -0
- data/.rubocop.yml +29 -0
- data/Gemfile +8 -0
- data/README.md +106 -0
- data/Rakefile +9 -0
- data/bin/console +15 -0
- data/bin/setup +8 -0
- data/lib/relax2/base.rb +33 -0
- data/lib/relax2/dsl.rb +25 -0
- data/lib/relax2/errors.rb +6 -0
- data/lib/relax2/file_cache.rb +31 -0
- data/lib/relax2/fluent_arg.rb +127 -0
- data/lib/relax2/interceptors.rb +78 -0
- data/lib/relax2/main.rb +22 -0
- data/lib/relax2/request.rb +30 -0
- data/lib/relax2/request_context.rb +82 -0
- data/lib/relax2/response.rb +10 -0
- data/lib/relax2/version.rb +5 -0
- data/lib/relax2.rb +3 -0
- data/relax2.gemspec +36 -0
- metadata +201 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA256:
|
3
|
+
metadata.gz: f3ced21a4e66c7b6eee62f684be2f4960bf10512f9d6618414f5dd6bc750f41a
|
4
|
+
data.tar.gz: 94c2ebd8799612e6dbd780ff5e2eecb65810c4a1b50ec883098ff4e4e7611647
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 7d102b5eb07a687e9fbd92bb9297246d980c0f0101299124a64ed561d1316e351d3e150e72d174846b39fccfc63768580e3db1fc567daa8a7a63548b519165ef
|
7
|
+
data.tar.gz: 76a6bec28e312cafb5ca718aa3109930142fe26dde56cb509613c8ceda521d582357c0a62a3b559ac1458e3bbcbb5e91d72662ac6cb1309b9822583880b2e19b
|
data/.rubocop.yml
ADDED
@@ -0,0 +1,29 @@
|
|
1
|
+
require:
|
2
|
+
- rubocop-minitest
|
3
|
+
- rubocop-performance
|
4
|
+
|
5
|
+
AllCops:
|
6
|
+
TargetRubyVersion: 2.7
|
7
|
+
|
8
|
+
Layout/LineLength:
|
9
|
+
Max: 140
|
10
|
+
|
11
|
+
Metrics/AbcSize:
|
12
|
+
Exclude:
|
13
|
+
- 'test/**/*'
|
14
|
+
|
15
|
+
Metrics/MethodLength:
|
16
|
+
Max: 16
|
17
|
+
|
18
|
+
Style/AccessModifierDeclarations:
|
19
|
+
EnforcedStyle: inline
|
20
|
+
|
21
|
+
Style/Documentation:
|
22
|
+
Exclude:
|
23
|
+
- 'test/**/*'
|
24
|
+
|
25
|
+
Style/TrailingCommaInArguments:
|
26
|
+
EnforcedStyleForMultiline: comma
|
27
|
+
|
28
|
+
Style/TrailingCommaInArrayLiteral:
|
29
|
+
EnforcedStyleForMultiline: comma
|
data/Gemfile
ADDED
data/README.md
ADDED
@@ -0,0 +1,106 @@
|
|
1
|
+
# Relax2
|
2
|
+
|
3
|
+
Relax2 is a quick and dirty HTTP API client factory for Ruby.
|
4
|
+
|
5
|
+
```ruby
|
6
|
+
## petstore.rb
|
7
|
+
require 'relax2'
|
8
|
+
|
9
|
+
base_url 'https://petstore.swagger.io/v2'
|
10
|
+
|
11
|
+
interceptor -> (request, perform_request) do
|
12
|
+
puts request.body
|
13
|
+
response = perform_request.call(request)
|
14
|
+
puts response.body
|
15
|
+
response
|
16
|
+
end
|
17
|
+
```
|
18
|
+
|
19
|
+
Then enjoy your API calls.
|
20
|
+
|
21
|
+
```
|
22
|
+
% ruby petstore.rb GET /pet/2
|
23
|
+
{
|
24
|
+
"id": 2,
|
25
|
+
"category": {
|
26
|
+
"id": 5,
|
27
|
+
"name": "Angel"
|
28
|
+
},
|
29
|
+
"name": "DOG",
|
30
|
+
"tags": [
|
31
|
+
{
|
32
|
+
"id": 1,
|
33
|
+
"name": "Armanda Hirthe"
|
34
|
+
}
|
35
|
+
],
|
36
|
+
"status": "available"
|
37
|
+
}
|
38
|
+
```
|
39
|
+
|
40
|
+
```
|
41
|
+
% ruby petstore.rb PUT /pet/2
|
42
|
+
{
|
43
|
+
"name": "NEW DOG"
|
44
|
+
}
|
45
|
+
|
46
|
+
{
|
47
|
+
"id": 2,
|
48
|
+
"category": {
|
49
|
+
"id": 5,
|
50
|
+
"name": "Angel"
|
51
|
+
},
|
52
|
+
"name": "NEW DOG",
|
53
|
+
"tags": [
|
54
|
+
{
|
55
|
+
"id": 1,
|
56
|
+
"name": "Armanda Hirthe"
|
57
|
+
}
|
58
|
+
],
|
59
|
+
"status": "available"
|
60
|
+
}
|
61
|
+
```
|
62
|
+
|
63
|
+
If you want to create more detailed client, use the modular style with `Relax2::Base`.
|
64
|
+
|
65
|
+
```ruby
|
66
|
+
require 'relax2/base'
|
67
|
+
|
68
|
+
class ExampleApi < Relax2::Base
|
69
|
+
base_url 'http://example.com/api/v1'
|
70
|
+
|
71
|
+
interceptor -> (request, perform_request) do
|
72
|
+
puts request.path
|
73
|
+
puts request.body
|
74
|
+
response = perform_request.call(request)
|
75
|
+
puts response.status
|
76
|
+
puts response.body
|
77
|
+
response
|
78
|
+
end
|
79
|
+
end
|
80
|
+
|
81
|
+
request = Relax2::Request.from_string('GET /hogehoge q=xx USER-Agent: Hoge/1.23')
|
82
|
+
response = ExampleApi.call(request)
|
83
|
+
```
|
84
|
+
|
85
|
+
|
86
|
+
|
87
|
+
|
88
|
+
## Installation
|
89
|
+
|
90
|
+
Add this line to your application's Gemfile:
|
91
|
+
|
92
|
+
```ruby
|
93
|
+
gem 'relax2'
|
94
|
+
```
|
95
|
+
|
96
|
+
and then `bundle install`
|
97
|
+
|
98
|
+
## Development
|
99
|
+
|
100
|
+
After checking out the repo, run `bin/setup` to install dependencies. Then, run `rake test` to run the tests. You can also run `bin/console` for an interactive prompt that will allow you to experiment.
|
101
|
+
|
102
|
+
To install this gem onto your local machine, run `bundle exec rake install`. To release a new version, update the version number in `version.rb`, and then run `bundle exec rake release`, which will create a git tag for the version, push git commits and tags, and push the `.gem` file to [rubygems.org](https://rubygems.org).
|
103
|
+
|
104
|
+
## Contributing
|
105
|
+
|
106
|
+
Bug reports and pull requests are welcome on GitHub at https://github.com/YusukeIwaki/relax2.
|
data/Rakefile
ADDED
data/bin/console
ADDED
@@ -0,0 +1,15 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
# frozen_string_literal: true
|
3
|
+
|
4
|
+
require 'bundler/setup'
|
5
|
+
require 'relax2'
|
6
|
+
|
7
|
+
# You can add fixtures and/or initialization code here to make experimenting
|
8
|
+
# with your gem easier. You can also use a different console, if you like.
|
9
|
+
|
10
|
+
# (If you use this, don't forget to add pry to your Gemfile!)
|
11
|
+
# require "pry"
|
12
|
+
# Pry.start
|
13
|
+
|
14
|
+
require 'irb'
|
15
|
+
IRB.start(__FILE__)
|
data/bin/setup
ADDED
data/lib/relax2/base.rb
ADDED
@@ -0,0 +1,33 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'relax2/dsl'
|
4
|
+
require 'relax2/errors'
|
5
|
+
require 'relax2/file_cache'
|
6
|
+
require 'relax2/fluent_arg'
|
7
|
+
require 'relax2/interceptors'
|
8
|
+
require 'relax2/request'
|
9
|
+
require 'relax2/request_context'
|
10
|
+
require 'relax2/response'
|
11
|
+
|
12
|
+
module Relax2
|
13
|
+
class Base
|
14
|
+
extend DSL
|
15
|
+
|
16
|
+
def self.call(request)
|
17
|
+
RequestContext.new(base_url: @base_url, interceptors: @interceptors).call(request)
|
18
|
+
end
|
19
|
+
|
20
|
+
def self.run
|
21
|
+
from_pipe = File.pipe?($stdin)
|
22
|
+
from_redirect = !IO.select([$stdin], [], [], 0).nil?
|
23
|
+
body = $stdin.read if from_pipe || from_redirect
|
24
|
+
|
25
|
+
request = Request.from(args: ARGV, body: body)
|
26
|
+
|
27
|
+
@interceptors ||= []
|
28
|
+
@interceptors << Interceptors.print_response if @interceptors.empty?
|
29
|
+
|
30
|
+
call(request)
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
data/lib/relax2/dsl.rb
ADDED
@@ -0,0 +1,25 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Relax2
|
4
|
+
module DSL
|
5
|
+
# @param base_url_value [String]
|
6
|
+
def base_url(base_url_value)
|
7
|
+
@base_url = base_url_value
|
8
|
+
end
|
9
|
+
|
10
|
+
# @param interceptor_impl [Proc|Symbol]
|
11
|
+
def interceptor(interceptor_impl = nil, &interceptor_impl_block)
|
12
|
+
impl =
|
13
|
+
if interceptor_impl_block
|
14
|
+
interceptor_impl_block
|
15
|
+
elsif interceptor_impl.is_a?(Symbol)
|
16
|
+
::Relax2::Interceptors.send(interceptor_impl)
|
17
|
+
elsif interceptor_impl.respond_to?(:call)
|
18
|
+
interceptor_impl
|
19
|
+
else
|
20
|
+
raise ArgumentError, 'interceptor must be specified as Proc or block'
|
21
|
+
end
|
22
|
+
(@interceptors ||= []) << impl
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
@@ -0,0 +1,31 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Relax2
|
4
|
+
class FileCache
|
5
|
+
def initialize(dirname, filename)
|
6
|
+
@cache_dir = cache_dir(dirname)
|
7
|
+
@cache_path = File.join(@cache_dir, filename)
|
8
|
+
end
|
9
|
+
|
10
|
+
private def cache_dir(name)
|
11
|
+
if Gem.win_platform?
|
12
|
+
File.join(Dir.home, 'AppData', 'Roaming', name)
|
13
|
+
else
|
14
|
+
File.join(Dir.home, ".#{name}")
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
def load
|
19
|
+
File.read(@cache_path) if Dir.exist?(@cache_dir) && File.exist?(@cache_path)
|
20
|
+
end
|
21
|
+
|
22
|
+
def save(data)
|
23
|
+
Dir.mkdir(@cache_dir) unless Dir.exist?(@cache_dir)
|
24
|
+
File.write(@cache_path, data)
|
25
|
+
end
|
26
|
+
|
27
|
+
def clear
|
28
|
+
File.delete(@cache_path) if File.exist?(@cache_path)
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
@@ -0,0 +1,127 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'uri'
|
4
|
+
|
5
|
+
module Relax2
|
6
|
+
# @internal
|
7
|
+
class FluentArg
|
8
|
+
NameValuePair = Struct.new(:name, :value) do
|
9
|
+
def initialize(...)
|
10
|
+
super
|
11
|
+
freeze
|
12
|
+
end
|
13
|
+
|
14
|
+
def to_a
|
15
|
+
[name, value]
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
# @param args [Array<String>] ['GET', '/search', 'q=dart', 'Authorization:' 'Basic' 'aXdha2k6MTIzNDUK']
|
20
|
+
def initialize(args)
|
21
|
+
maybe_http_method, maybe_path_with_query, *maybe_args = args
|
22
|
+
@http_method = validated_http_method_for(maybe_http_method)
|
23
|
+
path_with_query = validated_path_for(maybe_path_with_query)
|
24
|
+
params_and_headers = parse_params_and_headers_from(maybe_args)
|
25
|
+
|
26
|
+
# Merge query parameters
|
27
|
+
uri = URI.parse(path_with_query)
|
28
|
+
@query_parameters = query_parameters_from(uri) + params_and_headers.query_parameters
|
29
|
+
@path = uri.path
|
30
|
+
@headers = params_and_headers.headers
|
31
|
+
@magic_parameters = validated_magic_parameters_from(params_and_headers.magic_parameters)
|
32
|
+
end
|
33
|
+
|
34
|
+
attr_reader :http_method, :path, :query_parameters, :headers, :magic_parameters
|
35
|
+
|
36
|
+
private def validated_http_method_for(maybe_http_method)
|
37
|
+
raise InvalidArgError, 'HTTP method (GET, POST, ...) must be specified' unless Request::SUPPORTED_METHODS.include?(maybe_http_method)
|
38
|
+
|
39
|
+
maybe_http_method
|
40
|
+
end
|
41
|
+
|
42
|
+
private def validated_path_for(maybe_path_with_query)
|
43
|
+
maybe_path_with_query or raise InvalidArgError, 'path must be specified'
|
44
|
+
end
|
45
|
+
|
46
|
+
# @return [Array<Relax2::NameValuePair>]
|
47
|
+
private def query_parameters_from(base_uri)
|
48
|
+
URI.decode_www_form(base_uri.query || '').map do |name, value|
|
49
|
+
NameValuePair.new(name, value)
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
private def validated_magic_parameters_from(magic_parameters)
|
54
|
+
# Extract @body
|
55
|
+
magic_parameters.each_with_object({}) do |param, result|
|
56
|
+
raise InvalidArgError, "Unknown parameter: #{param.name}" unless param.name == '@body'
|
57
|
+
|
58
|
+
result[:body] = param.value
|
59
|
+
end
|
60
|
+
end
|
61
|
+
|
62
|
+
ParseResult = Struct.new(:query_parameters, :magic_parameters, :headers) do
|
63
|
+
def initialize(...)
|
64
|
+
super
|
65
|
+
freeze
|
66
|
+
end
|
67
|
+
end
|
68
|
+
|
69
|
+
private def match_parameters(arg)
|
70
|
+
if (match = /^(@?[a-zA-Z_]+)=/.match(arg))
|
71
|
+
name = match[1]
|
72
|
+
value = arg[match.end(0)..]
|
73
|
+
|
74
|
+
yield(name, value)
|
75
|
+
|
76
|
+
true
|
77
|
+
else
|
78
|
+
false
|
79
|
+
end
|
80
|
+
end
|
81
|
+
|
82
|
+
private def match_headers(arg)
|
83
|
+
if (match = /^([a-zA-Z_-]+):/.match(arg))
|
84
|
+
name = match[1]
|
85
|
+
value = arg[match.end(0)..]
|
86
|
+
|
87
|
+
yield(name, value)
|
88
|
+
|
89
|
+
true
|
90
|
+
else
|
91
|
+
false
|
92
|
+
end
|
93
|
+
end
|
94
|
+
|
95
|
+
# rubocop:disable Style/AbcSize, Metrics/MethodLength
|
96
|
+
private def parse_params_and_headers_from(maybe_args)
|
97
|
+
query_parameters = []
|
98
|
+
magic_parameters = []
|
99
|
+
headers = []
|
100
|
+
cur = nil
|
101
|
+
|
102
|
+
maybe_args.each do |arg|
|
103
|
+
next if match_parameters(arg) do |name, value|
|
104
|
+
cur =
|
105
|
+
if name.start_with?('@')
|
106
|
+
magic_parameters
|
107
|
+
else
|
108
|
+
query_parameters
|
109
|
+
end
|
110
|
+
|
111
|
+
cur << NameValuePair.new(name, value)
|
112
|
+
end
|
113
|
+
|
114
|
+
next if match_headers(arg) do |name, value|
|
115
|
+
cur = headers
|
116
|
+
cur << NameValuePair.new(name, value)
|
117
|
+
end
|
118
|
+
|
119
|
+
last = cur&.pop or raise InvalidArgError, "Unable to parse args: #{args.join(' ')}"
|
120
|
+
cur << NameValuePair.new(last.name, "#{last.value} #{arg}".lstrip)
|
121
|
+
end
|
122
|
+
|
123
|
+
ParseResult.new(query_parameters, magic_parameters, headers)
|
124
|
+
end
|
125
|
+
# rubocop:enable Style/AbcSize, Metrics/MethodLength
|
126
|
+
end
|
127
|
+
end
|
@@ -0,0 +1,78 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Relax2
|
4
|
+
module Interceptors
|
5
|
+
class PrintRequest
|
6
|
+
def initialize(print_headers:, print_body:)
|
7
|
+
@print_headers = print_headers
|
8
|
+
@print_body = print_body
|
9
|
+
end
|
10
|
+
|
11
|
+
def call(request, perform_request)
|
12
|
+
lines = []
|
13
|
+
lines << "#{request.http_method} #{request.path}"
|
14
|
+
if @print_headers
|
15
|
+
request.headers.each do |header|
|
16
|
+
lines << "#{header.name}: #{header.value}"
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
if @print_body && request.body
|
21
|
+
lines << ''
|
22
|
+
lines << request.body
|
23
|
+
end
|
24
|
+
|
25
|
+
puts lines.join("\n")
|
26
|
+
puts ''
|
27
|
+
|
28
|
+
perform_request.call(request)
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
class PrintResponse
|
33
|
+
def initialize(print_status:, print_headers:)
|
34
|
+
@print_status = print_status
|
35
|
+
@print_headers = print_headers
|
36
|
+
end
|
37
|
+
|
38
|
+
def call(request, perform_request)
|
39
|
+
lines = []
|
40
|
+
response = perform_request.call(request)
|
41
|
+
|
42
|
+
lines << "HTTP #{response.status}" if @print_status
|
43
|
+
|
44
|
+
if @print_headers
|
45
|
+
response.headers.each do |header|
|
46
|
+
lines << "#{header.name}: #{header.value}"
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
if response.body
|
51
|
+
lines << '' unless lines.empty?
|
52
|
+
lines << response.body
|
53
|
+
end
|
54
|
+
puts lines.join("\n")
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
58
|
+
module_function def verbose_print_request
|
59
|
+
PrintRequest.new(print_headers: true, print_body: true)
|
60
|
+
end
|
61
|
+
|
62
|
+
module_function def print_response
|
63
|
+
PrintResponse.new(print_status: false, print_headers: false)
|
64
|
+
end
|
65
|
+
|
66
|
+
module_function def verbose_print_response
|
67
|
+
PrintResponse.new(print_status: true, print_headers: true)
|
68
|
+
end
|
69
|
+
|
70
|
+
module_function def json_request
|
71
|
+
lambda do |request, perform_request|
|
72
|
+
request.headers << ['Accept', 'application/json']
|
73
|
+
request.headers << ['Content-Type', 'application/json']
|
74
|
+
perform_request.call(request)
|
75
|
+
end
|
76
|
+
end
|
77
|
+
end
|
78
|
+
end
|
data/lib/relax2/main.rb
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'English'
|
4
|
+
module Relax2
|
5
|
+
require 'relax2/base'
|
6
|
+
|
7
|
+
class MainApplication < Base
|
8
|
+
end
|
9
|
+
|
10
|
+
at_exit { MainApplication.run if $ERROR_INFO.nil? }
|
11
|
+
end
|
12
|
+
|
13
|
+
# Forward DSL methods into MainApplication class.
|
14
|
+
# `extend` is important here for defining DSL only into `main` object.
|
15
|
+
extend(Module.new do
|
16
|
+
Relax2::DSL.instance_methods(false).each do |method_name|
|
17
|
+
define_method(method_name) do |*args, **kwargs, &block|
|
18
|
+
Relax2::MainApplication.send(method_name, *args, **kwargs, &block)
|
19
|
+
end
|
20
|
+
private method_name
|
21
|
+
end
|
22
|
+
end)
|
@@ -0,0 +1,30 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Relax2
|
4
|
+
# Stores abstract request intents for building Net::HTTP::Request
|
5
|
+
Request = Struct.new(:http_method, :path, :query_parameters, :headers, :body, keyword_init: true) do
|
6
|
+
# @param args [Array<String>] assumed to be CLI args. ['GET', '/hoge', 'Authorization:', 'Bearer' 'mytopsecret']
|
7
|
+
# @param body [String|nil] assumed to be CLI PIPE input.
|
8
|
+
def self.from(args:, body: nil)
|
9
|
+
arg = FluentArg.new(args)
|
10
|
+
|
11
|
+
raise InvalidArgError, 'STDIN is not acceptable when @body is specified' if arg.magic_parameters[:body] && body
|
12
|
+
|
13
|
+
body = arg.magic_parameters[:body] ? IO.read(arg.magic_parameters[:body]) : body
|
14
|
+
|
15
|
+
new(
|
16
|
+
http_method: arg.http_method,
|
17
|
+
path: arg.path,
|
18
|
+
query_parameters: arg.query_parameters.map(&:to_a),
|
19
|
+
headers: arg.headers.map(&:to_a),
|
20
|
+
body: body,
|
21
|
+
)
|
22
|
+
end
|
23
|
+
|
24
|
+
def initialize(...)
|
25
|
+
super
|
26
|
+
freeze
|
27
|
+
end
|
28
|
+
end
|
29
|
+
Request::SUPPORTED_METHODS = %w[GET POST PUT PATCH DELETE].freeze
|
30
|
+
end
|
@@ -0,0 +1,82 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'net/http'
|
4
|
+
|
5
|
+
module Relax2
|
6
|
+
class RequestContext
|
7
|
+
def initialize(base_url:, interceptors:)
|
8
|
+
raise InvalidArgError, 'base_url must be specified' unless base_url
|
9
|
+
|
10
|
+
@base_url = base_url
|
11
|
+
@interceptors = interceptors
|
12
|
+
end
|
13
|
+
|
14
|
+
# @param request [Relax2::Request]
|
15
|
+
# @return [Relax2::Response]
|
16
|
+
def call(request)
|
17
|
+
original_method = ActualHttpRequestHandler.new(@base_url)
|
18
|
+
call_with_interceptors = @interceptors.reduce(original_method) do |m, interceptor|
|
19
|
+
->(req) { interceptor.call(req, m) }
|
20
|
+
end
|
21
|
+
call_with_interceptors.call(request)
|
22
|
+
end
|
23
|
+
|
24
|
+
NET_HTTP_REQUEST_MAP = {
|
25
|
+
'GET' => Net::HTTP::Get,
|
26
|
+
'POST' => Net::HTTP::Post,
|
27
|
+
'PUT' => Net::HTTP::Put,
|
28
|
+
'PATCH' => Net::HTTP::Patch,
|
29
|
+
'DELETE' => Net::HTTP::Delete
|
30
|
+
}.freeze
|
31
|
+
|
32
|
+
class ActualHttpRequestHandler
|
33
|
+
def initialize(base_url)
|
34
|
+
@base_url = base_url
|
35
|
+
end
|
36
|
+
|
37
|
+
# @param [Relax::Request]
|
38
|
+
# @return [Relas2::Response]
|
39
|
+
def call(request)
|
40
|
+
net_http_request = net_http_request_from(request)
|
41
|
+
net_http_response = start_net_http_session do |http|
|
42
|
+
http.request(net_http_request)
|
43
|
+
end
|
44
|
+
response_from(net_http_response)
|
45
|
+
end
|
46
|
+
|
47
|
+
private def start_net_http_session(&block)
|
48
|
+
uri = URI.parse(@base_url)
|
49
|
+
Net::HTTP.start(uri.host, uri.port, use_ssl: uri.scheme == 'https', &block)
|
50
|
+
end
|
51
|
+
|
52
|
+
private def request_uri_from(request)
|
53
|
+
URI.parse("#{@base_url}#{request.path}").tap do |uri|
|
54
|
+
uri.query = URI.encode_www_form(request.query_parameters) unless request.query_parameters.empty?
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
58
|
+
private def net_http_request_from(request)
|
59
|
+
net_http_request = NET_HTTP_REQUEST_MAP[request.http_method].new(request_uri_from(request))
|
60
|
+
|
61
|
+
request.headers.each do |name, value|
|
62
|
+
net_http_request[name] = value
|
63
|
+
end
|
64
|
+
net_http_request.body = request.body if request.body
|
65
|
+
net_http_request
|
66
|
+
end
|
67
|
+
|
68
|
+
private def response_from(net_http_response)
|
69
|
+
headers = []
|
70
|
+
net_http_response.each_header do |name, value|
|
71
|
+
headers << [name, value]
|
72
|
+
end
|
73
|
+
|
74
|
+
Response.new(
|
75
|
+
status: net_http_response.code.to_i,
|
76
|
+
headers: headers,
|
77
|
+
body: net_http_response.body,
|
78
|
+
)
|
79
|
+
end
|
80
|
+
end
|
81
|
+
end
|
82
|
+
end
|
data/lib/relax2.rb
ADDED
data/relax2.gemspec
ADDED
@@ -0,0 +1,36 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
lib = File.expand_path('lib', __dir__)
|
4
|
+
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
5
|
+
require 'relax2/version'
|
6
|
+
|
7
|
+
Gem::Specification.new do |spec|
|
8
|
+
spec.name = 'relax2'
|
9
|
+
spec.version = Relax2::VERSION
|
10
|
+
spec.authors = ['YusukeIwaki']
|
11
|
+
spec.email = ['q7w8e9w8q7w8e9@yahoo.co.jp']
|
12
|
+
|
13
|
+
spec.summary = 'A quick and dirty HTTP API client factory for Ruby'
|
14
|
+
spec.homepage = 'https://github.com/YusukeIwaki/relax2'
|
15
|
+
|
16
|
+
spec.files = Dir.chdir(File.expand_path(__dir__)) do
|
17
|
+
`git ls-files -z`.split("\x0").reject do |f|
|
18
|
+
f.match(%r{^(test|spec|features)/}) || f.include?('.git')
|
19
|
+
end
|
20
|
+
end
|
21
|
+
spec.bindir = 'exe'
|
22
|
+
spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
|
23
|
+
spec.require_paths = ['lib']
|
24
|
+
|
25
|
+
spec.required_ruby_version = '>= 2.7'
|
26
|
+
spec.add_development_dependency 'bundler'
|
27
|
+
spec.add_development_dependency 'minitest'
|
28
|
+
spec.add_development_dependency 'pry-byebug'
|
29
|
+
spec.add_development_dependency 'puma'
|
30
|
+
spec.add_development_dependency 'rack-test_server'
|
31
|
+
spec.add_development_dependency 'rake'
|
32
|
+
spec.add_development_dependency 'rubocop'
|
33
|
+
spec.add_development_dependency 'rubocop-minitest'
|
34
|
+
spec.add_development_dependency 'rubocop-performance'
|
35
|
+
spec.add_development_dependency 'sinatra'
|
36
|
+
end
|
metadata
ADDED
@@ -0,0 +1,201 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: relax2
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.1.0
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- YusukeIwaki
|
8
|
+
autorequire:
|
9
|
+
bindir: exe
|
10
|
+
cert_chain: []
|
11
|
+
date: 2022-07-18 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: bundler
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - ">="
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: '0'
|
20
|
+
type: :development
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - ">="
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: '0'
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: minitest
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - ">="
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: '0'
|
34
|
+
type: :development
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - ">="
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: '0'
|
41
|
+
- !ruby/object:Gem::Dependency
|
42
|
+
name: pry-byebug
|
43
|
+
requirement: !ruby/object:Gem::Requirement
|
44
|
+
requirements:
|
45
|
+
- - ">="
|
46
|
+
- !ruby/object:Gem::Version
|
47
|
+
version: '0'
|
48
|
+
type: :development
|
49
|
+
prerelease: false
|
50
|
+
version_requirements: !ruby/object:Gem::Requirement
|
51
|
+
requirements:
|
52
|
+
- - ">="
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: '0'
|
55
|
+
- !ruby/object:Gem::Dependency
|
56
|
+
name: puma
|
57
|
+
requirement: !ruby/object:Gem::Requirement
|
58
|
+
requirements:
|
59
|
+
- - ">="
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: '0'
|
62
|
+
type: :development
|
63
|
+
prerelease: false
|
64
|
+
version_requirements: !ruby/object:Gem::Requirement
|
65
|
+
requirements:
|
66
|
+
- - ">="
|
67
|
+
- !ruby/object:Gem::Version
|
68
|
+
version: '0'
|
69
|
+
- !ruby/object:Gem::Dependency
|
70
|
+
name: rack-test_server
|
71
|
+
requirement: !ruby/object:Gem::Requirement
|
72
|
+
requirements:
|
73
|
+
- - ">="
|
74
|
+
- !ruby/object:Gem::Version
|
75
|
+
version: '0'
|
76
|
+
type: :development
|
77
|
+
prerelease: false
|
78
|
+
version_requirements: !ruby/object:Gem::Requirement
|
79
|
+
requirements:
|
80
|
+
- - ">="
|
81
|
+
- !ruby/object:Gem::Version
|
82
|
+
version: '0'
|
83
|
+
- !ruby/object:Gem::Dependency
|
84
|
+
name: rake
|
85
|
+
requirement: !ruby/object:Gem::Requirement
|
86
|
+
requirements:
|
87
|
+
- - ">="
|
88
|
+
- !ruby/object:Gem::Version
|
89
|
+
version: '0'
|
90
|
+
type: :development
|
91
|
+
prerelease: false
|
92
|
+
version_requirements: !ruby/object:Gem::Requirement
|
93
|
+
requirements:
|
94
|
+
- - ">="
|
95
|
+
- !ruby/object:Gem::Version
|
96
|
+
version: '0'
|
97
|
+
- !ruby/object:Gem::Dependency
|
98
|
+
name: rubocop
|
99
|
+
requirement: !ruby/object:Gem::Requirement
|
100
|
+
requirements:
|
101
|
+
- - ">="
|
102
|
+
- !ruby/object:Gem::Version
|
103
|
+
version: '0'
|
104
|
+
type: :development
|
105
|
+
prerelease: false
|
106
|
+
version_requirements: !ruby/object:Gem::Requirement
|
107
|
+
requirements:
|
108
|
+
- - ">="
|
109
|
+
- !ruby/object:Gem::Version
|
110
|
+
version: '0'
|
111
|
+
- !ruby/object:Gem::Dependency
|
112
|
+
name: rubocop-minitest
|
113
|
+
requirement: !ruby/object:Gem::Requirement
|
114
|
+
requirements:
|
115
|
+
- - ">="
|
116
|
+
- !ruby/object:Gem::Version
|
117
|
+
version: '0'
|
118
|
+
type: :development
|
119
|
+
prerelease: false
|
120
|
+
version_requirements: !ruby/object:Gem::Requirement
|
121
|
+
requirements:
|
122
|
+
- - ">="
|
123
|
+
- !ruby/object:Gem::Version
|
124
|
+
version: '0'
|
125
|
+
- !ruby/object:Gem::Dependency
|
126
|
+
name: rubocop-performance
|
127
|
+
requirement: !ruby/object:Gem::Requirement
|
128
|
+
requirements:
|
129
|
+
- - ">="
|
130
|
+
- !ruby/object:Gem::Version
|
131
|
+
version: '0'
|
132
|
+
type: :development
|
133
|
+
prerelease: false
|
134
|
+
version_requirements: !ruby/object:Gem::Requirement
|
135
|
+
requirements:
|
136
|
+
- - ">="
|
137
|
+
- !ruby/object:Gem::Version
|
138
|
+
version: '0'
|
139
|
+
- !ruby/object:Gem::Dependency
|
140
|
+
name: sinatra
|
141
|
+
requirement: !ruby/object:Gem::Requirement
|
142
|
+
requirements:
|
143
|
+
- - ">="
|
144
|
+
- !ruby/object:Gem::Version
|
145
|
+
version: '0'
|
146
|
+
type: :development
|
147
|
+
prerelease: false
|
148
|
+
version_requirements: !ruby/object:Gem::Requirement
|
149
|
+
requirements:
|
150
|
+
- - ">="
|
151
|
+
- !ruby/object:Gem::Version
|
152
|
+
version: '0'
|
153
|
+
description:
|
154
|
+
email:
|
155
|
+
- q7w8e9w8q7w8e9@yahoo.co.jp
|
156
|
+
executables: []
|
157
|
+
extensions: []
|
158
|
+
extra_rdoc_files: []
|
159
|
+
files:
|
160
|
+
- ".rubocop.yml"
|
161
|
+
- Gemfile
|
162
|
+
- README.md
|
163
|
+
- Rakefile
|
164
|
+
- bin/console
|
165
|
+
- bin/setup
|
166
|
+
- lib/relax2.rb
|
167
|
+
- lib/relax2/base.rb
|
168
|
+
- lib/relax2/dsl.rb
|
169
|
+
- lib/relax2/errors.rb
|
170
|
+
- lib/relax2/file_cache.rb
|
171
|
+
- lib/relax2/fluent_arg.rb
|
172
|
+
- lib/relax2/interceptors.rb
|
173
|
+
- lib/relax2/main.rb
|
174
|
+
- lib/relax2/request.rb
|
175
|
+
- lib/relax2/request_context.rb
|
176
|
+
- lib/relax2/response.rb
|
177
|
+
- lib/relax2/version.rb
|
178
|
+
- relax2.gemspec
|
179
|
+
homepage: https://github.com/YusukeIwaki/relax2
|
180
|
+
licenses: []
|
181
|
+
metadata: {}
|
182
|
+
post_install_message:
|
183
|
+
rdoc_options: []
|
184
|
+
require_paths:
|
185
|
+
- lib
|
186
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
187
|
+
requirements:
|
188
|
+
- - ">="
|
189
|
+
- !ruby/object:Gem::Version
|
190
|
+
version: '2.7'
|
191
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
192
|
+
requirements:
|
193
|
+
- - ">="
|
194
|
+
- !ruby/object:Gem::Version
|
195
|
+
version: '0'
|
196
|
+
requirements: []
|
197
|
+
rubygems_version: 3.3.7
|
198
|
+
signing_key:
|
199
|
+
specification_version: 4
|
200
|
+
summary: A quick and dirty HTTP API client factory for Ruby
|
201
|
+
test_files: []
|