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 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
@@ -0,0 +1,8 @@
1
+ # frozen_string_literal: true
2
+
3
+ source 'https://rubygems.org'
4
+
5
+ git_source(:github) { |repo_name| "https://github.com/#{repo_name}" }
6
+
7
+ # Specify your gem's dependencies in relax2.gemspec
8
+ gemspec
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
@@ -0,0 +1,9 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'bundler/gem_tasks'
4
+ require 'rake/testtask'
5
+
6
+ Rake::TestTask.new(:test) do |t|
7
+ t.libs << 'lib'
8
+ t.test_files = FileList['test/**/*_test.rb']
9
+ end
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
@@ -0,0 +1,8 @@
1
+ #!/usr/bin/env bash
2
+ set -euo pipefail
3
+ IFS=$'\n\t'
4
+ set -vx
5
+
6
+ bundle install
7
+
8
+ # Do any other automated setup that you need to do here
@@ -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,6 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Relax2
4
+ class InvalidArgError < RuntimeError
5
+ end
6
+ 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
@@ -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
@@ -0,0 +1,10 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Relax2
4
+ Response = Struct.new(:status, :headers, :body, keyword_init: true) do
5
+ def initialize(...)
6
+ super
7
+ freeze
8
+ end
9
+ end
10
+ end
@@ -0,0 +1,5 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Relax2
4
+ VERSION = '0.1.0'
5
+ end
data/lib/relax2.rb ADDED
@@ -0,0 +1,3 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'relax2/main'
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: []