relax2 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
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: []