restool 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/lib/restool.rb +13 -0
- data/lib/restool/service/operation_definer.rb +31 -0
- data/lib/restool/service/remote_client.rb +50 -0
- data/lib/restool/service/remote_connector.rb +24 -0
- data/lib/restool/service/request_utils.rb +46 -0
- data/lib/restool/service/restool_service.rb +44 -0
- data/lib/restool/service/uri_utils.rb +20 -0
- data/lib/restool/settings/loader.rb +136 -0
- data/lib/restool/settings/models.rb +16 -0
- data/lib/restool/traversal/converter.rb +91 -0
- data/lib/restool/traversal/object.rb +17 -0
- data/lib/restool/version.rb +3 -0
- metadata +70 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA256:
|
3
|
+
metadata.gz: 00b59168b2f8bf6dda90e1764dfb4e86b31fc35b9467a6dbe128fa456747cfe3
|
4
|
+
data.tar.gz: '09267a1659146bfafdc1433ea3164f071f8f9af8325c405dbb145918ec630aa5'
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: bc937386969581255334f5c648b1241c2eb59867f54cb4eaa2c968c6a3f1a1c29d781ef8ad621a554a4c21a2e35f576d20c7de0ba873c8383db784596f318943
|
7
|
+
data.tar.gz: 6dae0d57818581ce78f335b814adc9921f81ffa5279d7848b0612dc8b657ba15226740c6e254df86e99f53c60e99c75c3fd99546a477d86dc600f860d408ea2b
|
data/lib/restool.rb
ADDED
@@ -0,0 +1,13 @@
|
|
1
|
+
require_relative 'restool/settings/loader'
|
2
|
+
require_relative 'restool/service/restool_service'
|
3
|
+
|
4
|
+
|
5
|
+
module Restool
|
6
|
+
|
7
|
+
def self.create(service_name, &response_handler)
|
8
|
+
service_config = Restool::Settings::Loader.load(service_name)
|
9
|
+
|
10
|
+
Restool::Service::RestoolService.new(service_config, response_handler)
|
11
|
+
end
|
12
|
+
|
13
|
+
end
|
@@ -0,0 +1,31 @@
|
|
1
|
+
require_relative 'uri_utils'
|
2
|
+
|
3
|
+
module Restool
|
4
|
+
module Service
|
5
|
+
module OperationDefiner
|
6
|
+
|
7
|
+
def define_operations(service_config, method_make_request, method_make_request_with_uri_params)
|
8
|
+
service_config.operations.each do |operation|
|
9
|
+
if operation.uri_params != []
|
10
|
+
define_request_method_with_uri_params(operation, method_make_request_with_uri_params)
|
11
|
+
else
|
12
|
+
define_request_method(operation, method_make_request)
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
def define_request_method_with_uri_params(operation, method_make_request_with_uri_params)
|
18
|
+
define_singleton_method(operation.name) do |uri_params_values, *params|
|
19
|
+
method_make_request_with_uri_params.call(operation, uri_params_values, params[0], params[1])
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
def define_request_method(operation, method_make_request)
|
24
|
+
define_singleton_method(operation.name) do |*params|
|
25
|
+
method_make_request.call(operation, params[0], params[1])
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
@@ -0,0 +1,50 @@
|
|
1
|
+
require 'persistent_http'
|
2
|
+
|
3
|
+
require_relative 'request_utils'
|
4
|
+
|
5
|
+
module Restool
|
6
|
+
module Service
|
7
|
+
class RemoteClient
|
8
|
+
|
9
|
+
def initialize(host, verify_ssl, persistent_connection, timeout)
|
10
|
+
@connection = if persistent_connection
|
11
|
+
PersistentHTTP.new(
|
12
|
+
pool_size: persistent_connection.pool_size,
|
13
|
+
pool_timeout: timeout,
|
14
|
+
warn_timeout: persistent_connection.warn_timeout,
|
15
|
+
force_retry: persistent_connection.force_retry,
|
16
|
+
url: host,
|
17
|
+
read_timeout: timeout,
|
18
|
+
open_timeout: timeout,
|
19
|
+
verify_mode: verify_ssl?(verify_ssl)
|
20
|
+
)
|
21
|
+
else
|
22
|
+
uri = URI.parse(host)
|
23
|
+
http = Net::HTTP.new(uri.host, uri.port)
|
24
|
+
http.use_ssl = ssl_implied?(uri)
|
25
|
+
http.verify_mode = verify_ssl?(verify_ssl)
|
26
|
+
http.read_timeout = timeout
|
27
|
+
http.open_timeout = timeout
|
28
|
+
# http.set_debug_output($stdout)
|
29
|
+
http
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
def make_request(path, method, request_params, headers, basic_auth)
|
34
|
+
request = RequestUtils.build_request(method, path, request_params, headers, basic_auth)
|
35
|
+
|
36
|
+
@connection.request(request)
|
37
|
+
end
|
38
|
+
|
39
|
+
private
|
40
|
+
|
41
|
+
def ssl_implied?(uri)
|
42
|
+
uri.port == 443 || uri.scheme == 'https'
|
43
|
+
end
|
44
|
+
|
45
|
+
def verify_ssl?(verify_ssl_setting)
|
46
|
+
verify_ssl_setting ? OpenSSL::SSL::VERIFY_PEER : OpenSSL::SSL::VERIFY_NONE
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end
|
@@ -0,0 +1,24 @@
|
|
1
|
+
require_relative '../traversal/converter'
|
2
|
+
|
3
|
+
module Restool
|
4
|
+
module Service
|
5
|
+
module RemoteConnector
|
6
|
+
# The RemoteConnector module makes the requests using the RemoteClient,
|
7
|
+
# calls the response_handler with the response, and finally executes
|
8
|
+
# the object traversal
|
9
|
+
|
10
|
+
def self.execute(remote_client, operation, path, params, headers,
|
11
|
+
response_handler, representations, basic_auth)
|
12
|
+
remote_response = remote_client.make_request(path, operation.method, params, headers,
|
13
|
+
basic_auth)
|
14
|
+
|
15
|
+
response = response_handler.call(remote_response.body, remote_response.code)
|
16
|
+
|
17
|
+
return response if operation.response.nil?
|
18
|
+
|
19
|
+
Restool::Traversal::Converter.convert(response, operation.response, representations)
|
20
|
+
end
|
21
|
+
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
@@ -0,0 +1,46 @@
|
|
1
|
+
module Restool
|
2
|
+
module Service
|
3
|
+
module RequestUtils
|
4
|
+
|
5
|
+
def self.build_request(method, path, params, headers, basic_auth)
|
6
|
+
if ['post', 'put', 'patch'].include?(method)
|
7
|
+
request = build_base_request(method, path)
|
8
|
+
|
9
|
+
if params && params.is_a?(Hash)
|
10
|
+
request.set_form_data(params)
|
11
|
+
else
|
12
|
+
request.body = params
|
13
|
+
end
|
14
|
+
|
15
|
+
else
|
16
|
+
uri = URI(path)
|
17
|
+
uri.query = URI.encode_www_form(params) if params
|
18
|
+
|
19
|
+
request = build_base_request(method, uri.to_s)
|
20
|
+
end
|
21
|
+
|
22
|
+
request.basic_auth(basic_auth.user, basic_auth.password) if basic_auth
|
23
|
+
|
24
|
+
headers.each { |k, v| request[k] = v } if headers
|
25
|
+
|
26
|
+
request
|
27
|
+
end
|
28
|
+
|
29
|
+
def self.build_base_request(method, path)
|
30
|
+
case method.to_s.downcase
|
31
|
+
when 'get'
|
32
|
+
Net::HTTP::Get.new(path)
|
33
|
+
when 'post'
|
34
|
+
Net::HTTP::Post.new(path)
|
35
|
+
when 'put'
|
36
|
+
Net::HTTP::Put.new(path)
|
37
|
+
when 'delete'
|
38
|
+
Net::HTTP::Delete.new(path)
|
39
|
+
when 'patch'
|
40
|
+
Net::HTTP::Patch.new(path)
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
@@ -0,0 +1,44 @@
|
|
1
|
+
require_relative 'remote_client'
|
2
|
+
require_relative 'remote_connector'
|
3
|
+
require_relative 'operation_definer'
|
4
|
+
|
5
|
+
module Restool
|
6
|
+
module Service
|
7
|
+
class RestoolService
|
8
|
+
include Restool::Service::OperationDefiner
|
9
|
+
|
10
|
+
def initialize(service_config, response_handler)
|
11
|
+
@service_config = service_config
|
12
|
+
@response_handler = response_handler
|
13
|
+
@remote_client = Restool::Service::RemoteClient.new(service_config.host, service_config.verify_ssl,
|
14
|
+
service_config.persistent, service_config.timeout)
|
15
|
+
|
16
|
+
define_operations(
|
17
|
+
@service_config, method(:make_request), method(:make_request_with_uri_params)
|
18
|
+
)
|
19
|
+
end
|
20
|
+
|
21
|
+
private
|
22
|
+
|
23
|
+
# this methods are called directly from the client though the OperationDefiner
|
24
|
+
|
25
|
+
def make_request(operation, params, headers = {})
|
26
|
+
path = Restool::Service::UriUtils.build_path(operation)
|
27
|
+
|
28
|
+
Restool::Service::RemoteConnector.execute(
|
29
|
+
@remote_client, operation, path, params, headers, @response_handler,
|
30
|
+
@service_config.representations, @service_config.basic_auth
|
31
|
+
)
|
32
|
+
end
|
33
|
+
|
34
|
+
def make_request_with_uri_params(operation, uri_params_values, params, headers = {})
|
35
|
+
path = Restool::Service::UriUtils.build_path(operation, uri_params_values)
|
36
|
+
|
37
|
+
Restool::Service::RemoteConnector.execute(
|
38
|
+
@remote_client, operation, path, params, headers, @response_handler,
|
39
|
+
@service_config.representations, @service_config.basic_auth
|
40
|
+
)
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
@@ -0,0 +1,20 @@
|
|
1
|
+
module Restool
|
2
|
+
module Service
|
3
|
+
module UriUtils
|
4
|
+
|
5
|
+
def self.build_path(operation, uri_params_values = nil)
|
6
|
+
path = operation.path
|
7
|
+
|
8
|
+
if uri_params_values
|
9
|
+
operation.uri_params.each_with_index do |uri_param, i|
|
10
|
+
path.sub!(/#{uri_param}/, uri_params_values[i].to_s)
|
11
|
+
end
|
12
|
+
end
|
13
|
+
|
14
|
+
path
|
15
|
+
end
|
16
|
+
|
17
|
+
end
|
18
|
+
|
19
|
+
end
|
20
|
+
end
|
@@ -0,0 +1,136 @@
|
|
1
|
+
require 'yaml'
|
2
|
+
|
3
|
+
require_relative 'models'
|
4
|
+
|
5
|
+
module Restool
|
6
|
+
module Settings
|
7
|
+
module Loader
|
8
|
+
include Restool::Settings::Models
|
9
|
+
|
10
|
+
DEFAULT_TIMEOUT = 60
|
11
|
+
DEFAULT_SSL_VERIFY = false
|
12
|
+
|
13
|
+
|
14
|
+
def self.load(service_name)
|
15
|
+
service_config = config['services'].detect do |service|
|
16
|
+
service['name'] == service_name
|
17
|
+
end
|
18
|
+
|
19
|
+
raise "Service #{service_name} not found in configuration" unless service_config
|
20
|
+
|
21
|
+
build_service(service_config)
|
22
|
+
end
|
23
|
+
|
24
|
+
private
|
25
|
+
|
26
|
+
def self.build_service(service_config)
|
27
|
+
representations = if service_config['representations']
|
28
|
+
build_representations(service_config['representations'])
|
29
|
+
else
|
30
|
+
[]
|
31
|
+
end
|
32
|
+
|
33
|
+
basic_auth = service_config['basic_auth'] || service_config['basic_authentication']
|
34
|
+
basic_auth = BasicAuthentication.new(basic_auth['user'], basic_auth['password']) if basic_auth
|
35
|
+
|
36
|
+
persistent_connection = service_config['persistent']
|
37
|
+
persistent_connection = if persistent_connection
|
38
|
+
PersistentConnection.new(
|
39
|
+
persistent_connection['pool_size'],
|
40
|
+
persistent_connection['warn_timeout'],
|
41
|
+
persistent_connection['force_retry'],
|
42
|
+
)
|
43
|
+
end
|
44
|
+
|
45
|
+
# Support host + common path in url config, e.g. api.com/v2/
|
46
|
+
paths_prefix_in_host = URI(service_config['url']).path
|
47
|
+
|
48
|
+
Models::Service.new(
|
49
|
+
service_config['name'],
|
50
|
+
service_config['url'],
|
51
|
+
service_config['operations'].map { |operation| build_operation(operation, paths_prefix_in_host) },
|
52
|
+
persistent_connection,
|
53
|
+
service_config['timeout'] || DEFAULT_TIMEOUT,
|
54
|
+
representations,
|
55
|
+
basic_auth,
|
56
|
+
service_config['ssl_verify'] || DEFAULT_SSL_VERIFY
|
57
|
+
)
|
58
|
+
end
|
59
|
+
|
60
|
+
def self.build_representations(representations)
|
61
|
+
representations_by_name = {}
|
62
|
+
|
63
|
+
representations.each do |representation|
|
64
|
+
fields = representation[1].map do |field|
|
65
|
+
RepresentationField.new(field['key'],
|
66
|
+
field['metonym'],
|
67
|
+
field['type'].to_sym)
|
68
|
+
end
|
69
|
+
|
70
|
+
representation = Representation.new(name = representation.first, fields)
|
71
|
+
representations_by_name[representation.name.to_sym] = representation
|
72
|
+
end
|
73
|
+
|
74
|
+
representations_by_name
|
75
|
+
end
|
76
|
+
|
77
|
+
def self.build_operation(operation_config, paths_prefix_in_host)
|
78
|
+
response = build_operation_response(operation_config['response']) if operation_config['response']
|
79
|
+
|
80
|
+
path = operation_config['path']
|
81
|
+
path = path[1..-1] if path[0] == '/'
|
82
|
+
paths_prefix_in_host.chomp!('/')
|
83
|
+
|
84
|
+
Operation.new(
|
85
|
+
operation_config['name'],
|
86
|
+
"#{paths_prefix_in_host}/#{path}",
|
87
|
+
operation_config['method'],
|
88
|
+
uri_params(operation_config),
|
89
|
+
response
|
90
|
+
)
|
91
|
+
end
|
92
|
+
|
93
|
+
def self.build_operation_response(response)
|
94
|
+
response_fields = response.map do |field|
|
95
|
+
OperationResponsField.new(field['key'], field['metonym'], field['type'].to_sym)
|
96
|
+
end
|
97
|
+
|
98
|
+
OperationResponse.new(response_fields)
|
99
|
+
end
|
100
|
+
|
101
|
+
def self.uri_params(operation_config)
|
102
|
+
operation_config['path'].scan(/:[a-zA-Z_]+[0-9]*[a-zA-Z_]*/)
|
103
|
+
end
|
104
|
+
|
105
|
+
def self.config
|
106
|
+
return @config if @config
|
107
|
+
|
108
|
+
files_to_load = Dir['config/restool/*'] + ['config/restool.yml', 'config/restool.json']
|
109
|
+
|
110
|
+
@config = { 'services' => [] }
|
111
|
+
|
112
|
+
files_to_load.each do |file_name|
|
113
|
+
next unless File.exist?(file_name)
|
114
|
+
|
115
|
+
extension = File.extname(file_name)
|
116
|
+
|
117
|
+
content = if extension == '.yml'
|
118
|
+
YAML.load_file(file_name)
|
119
|
+
elsif extension == '.json'
|
120
|
+
json_file = File.read(file_name)
|
121
|
+
JSON.parse(json_file)
|
122
|
+
end
|
123
|
+
|
124
|
+
@config['services'] += content['services']
|
125
|
+
end
|
126
|
+
|
127
|
+
@config
|
128
|
+
end
|
129
|
+
|
130
|
+
def self.validate
|
131
|
+
# TODO: perform validations
|
132
|
+
end
|
133
|
+
|
134
|
+
end
|
135
|
+
end
|
136
|
+
end
|
@@ -0,0 +1,16 @@
|
|
1
|
+
module Restool
|
2
|
+
module Settings
|
3
|
+
module Models
|
4
|
+
|
5
|
+
Operation = Struct.new(:name, :path, :method, :uri_params, :response)
|
6
|
+
OperationResponse = Struct.new(:fields)
|
7
|
+
Service = Struct.new(:name, :host, :operations, :persistent, :timeout, :representations, :basic_auth, :verify_ssl)
|
8
|
+
Representation = Struct.new(:name, :fields)
|
9
|
+
RepresentationField = Struct.new(:key, :metonym, :type)
|
10
|
+
BasicAuthentication = Struct.new(:user, :password)
|
11
|
+
PersistentConnection = Struct.new(:respool_size, :want_timeout, :force_retry)
|
12
|
+
OperationResponsField = RepresentationField
|
13
|
+
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
@@ -0,0 +1,91 @@
|
|
1
|
+
require 'bigdecimal'
|
2
|
+
|
3
|
+
require_relative 'object'
|
4
|
+
|
5
|
+
module Restool
|
6
|
+
module Traversal
|
7
|
+
|
8
|
+
TRAVERSAL_TYPE_STRING = :string
|
9
|
+
TRAVERSAL_TYPE_INTEGER = :integer
|
10
|
+
TRAVERSAL_TYPE_DECIMAL = :decimal
|
11
|
+
TRAVERSAL_TYPE_BOOLEAN = :boolean
|
12
|
+
|
13
|
+
TRAVERSAL_TYPES = [
|
14
|
+
TRAVERSAL_TYPE_STRING, TRAVERSAL_TYPE_INTEGER, TRAVERSAL_TYPE_DECIMAL, TRAVERSAL_TYPE_BOOLEAN
|
15
|
+
]
|
16
|
+
|
17
|
+
module Converter
|
18
|
+
|
19
|
+
def self.convert(request_response, response_representation, representations)
|
20
|
+
object = Restool::Traversal::Object.new
|
21
|
+
|
22
|
+
if request_response.is_a?(Array)
|
23
|
+
request_response.map do |element|
|
24
|
+
map_response_to_representation(response_representation, element, object, representations)
|
25
|
+
end
|
26
|
+
else
|
27
|
+
map_response_to_representation(response_representation, request_response, object, representations)
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
private
|
32
|
+
|
33
|
+
def self.map_response_to_representation(representation, request_response, object, representations)
|
34
|
+
representation.fields.each do |field|
|
35
|
+
value = request_response[field.key]
|
36
|
+
|
37
|
+
object.class.__send__(:attr_accessor, var_name(field))
|
38
|
+
|
39
|
+
if Restool::Traversal::TRAVERSAL_TYPES.include?(field.type.to_sym)
|
40
|
+
map_primitive_field(value, field, object)
|
41
|
+
else
|
42
|
+
map_complex_field(value, field, object, representations)
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
object
|
47
|
+
end
|
48
|
+
|
49
|
+
def self.map_primitive_field(value, field, object)
|
50
|
+
new_value = if value.is_a?(Array)
|
51
|
+
value.map { |element| parse_value(field.type, element) }
|
52
|
+
else
|
53
|
+
parse_value(field.type, value)
|
54
|
+
end
|
55
|
+
|
56
|
+
object.__send__("#{var_name(field)}=", new_value)
|
57
|
+
end
|
58
|
+
|
59
|
+
def self.map_complex_field(value, field, object, representations)
|
60
|
+
operation_representation = representations[field.type.to_sym]
|
61
|
+
|
62
|
+
new_value = if value.is_a?(Array)
|
63
|
+
value.map { |element| convert(element, operation_representation, representations) }
|
64
|
+
else
|
65
|
+
convert(value, operation_representation, representations)
|
66
|
+
end
|
67
|
+
|
68
|
+
object.__send__("#{var_name(field)}=", new_value)
|
69
|
+
end
|
70
|
+
|
71
|
+
def self.var_name(field)
|
72
|
+
field.metonym || field.key
|
73
|
+
end
|
74
|
+
|
75
|
+
def self.parse_value(type, value)
|
76
|
+
case type
|
77
|
+
when Restool::Traversal::TRAVERSAL_TYPE_STRING
|
78
|
+
value
|
79
|
+
when Restool::Traversal::TRAVERSAL_TYPE_INTEGER
|
80
|
+
Integer(value)
|
81
|
+
when Restool::Traversal::TRAVERSAL_TYPE_DECIMAL
|
82
|
+
BigDecimal.new(scalar)
|
83
|
+
when Restool::Traversal::TRAVERSAL_TYPE_BOOLEAN
|
84
|
+
value.downcase == 'true'
|
85
|
+
end
|
86
|
+
end
|
87
|
+
|
88
|
+
end
|
89
|
+
|
90
|
+
end
|
91
|
+
end
|
@@ -0,0 +1,17 @@
|
|
1
|
+
module Restool
|
2
|
+
module Traversal
|
3
|
+
class Object
|
4
|
+
|
5
|
+
def to_hash
|
6
|
+
instance_variables
|
7
|
+
.inject({}) do |acum, var|
|
8
|
+
acum[var.to_s.delete('@')] = instance_variable_get(var)
|
9
|
+
acum
|
10
|
+
end
|
11
|
+
end
|
12
|
+
|
13
|
+
alias_method :to_h, :to_hash
|
14
|
+
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
metadata
ADDED
@@ -0,0 +1,70 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: restool
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.1.0
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Juan Andres Zeni
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2020-04-11 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: persistent_http
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - "~>"
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: '2'
|
20
|
+
type: :runtime
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - "~>"
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: '2'
|
27
|
+
description: Make HTTP requests and handle its responses using simple method calls
|
28
|
+
Restool turns your HTTP API and its responses into Ruby interfaces.
|
29
|
+
email:
|
30
|
+
- juanandreszeni@gmail.com
|
31
|
+
executables: []
|
32
|
+
extensions: []
|
33
|
+
extra_rdoc_files: []
|
34
|
+
files:
|
35
|
+
- lib/restool.rb
|
36
|
+
- lib/restool/service/operation_definer.rb
|
37
|
+
- lib/restool/service/remote_client.rb
|
38
|
+
- lib/restool/service/remote_connector.rb
|
39
|
+
- lib/restool/service/request_utils.rb
|
40
|
+
- lib/restool/service/restool_service.rb
|
41
|
+
- lib/restool/service/uri_utils.rb
|
42
|
+
- lib/restool/settings/loader.rb
|
43
|
+
- lib/restool/settings/models.rb
|
44
|
+
- lib/restool/traversal/converter.rb
|
45
|
+
- lib/restool/traversal/object.rb
|
46
|
+
- lib/restool/version.rb
|
47
|
+
homepage: https://github.com/jzeni/restool
|
48
|
+
licenses:
|
49
|
+
- MIT
|
50
|
+
metadata: {}
|
51
|
+
post_install_message:
|
52
|
+
rdoc_options: []
|
53
|
+
require_paths:
|
54
|
+
- lib
|
55
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
56
|
+
requirements:
|
57
|
+
- - ">="
|
58
|
+
- !ruby/object:Gem::Version
|
59
|
+
version: 2.3.0
|
60
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
61
|
+
requirements:
|
62
|
+
- - ">="
|
63
|
+
- !ruby/object:Gem::Version
|
64
|
+
version: '0'
|
65
|
+
requirements: []
|
66
|
+
rubygems_version: 3.0.1
|
67
|
+
signing_key:
|
68
|
+
specification_version: 4
|
69
|
+
summary: Turn your API and its responses into Ruby interfaces.
|
70
|
+
test_files: []
|