haveapi 0.23.7 → 0.25.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/README.md +3 -3
- data/haveapi.gemspec +4 -4
- data/lib/haveapi/action.rb +5 -5
- data/lib/haveapi/action_state.rb +2 -2
- data/lib/haveapi/actions/paginable.rb +3 -3
- data/lib/haveapi/authentication/oauth2/provider.rb +1 -1
- data/lib/haveapi/authentication/token/provider.rb +8 -8
- data/lib/haveapi/model_adapters/active_record.rb +41 -0
- data/lib/haveapi/params.rb +7 -0
- data/lib/haveapi/resources/action_state.rb +7 -7
- data/lib/haveapi/server.rb +15 -11
- data/lib/haveapi/spec/spec_methods.rb +2 -2
- data/lib/haveapi/version.rb +1 -1
- data/lib/haveapi/views/version_page/resource_body.erb +3 -8
- data/lib/haveapi/views/version_page.erb +0 -28
- metadata +9 -9
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 3e3abf24d477cb53a7654a236f2daf3abadd8b153d3017ea159dbe1dd072fd2b
|
4
|
+
data.tar.gz: 406961707ce35f79c6ee6f2835697143f2b5a4e42e86f433949d08f98b546644
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: bdddd4eb49bc38b54c799b2cd57668f550a38212f1b735f1aad6238bb93f4a7d3b0b1bc52ff09055d0247afd70fa6fe156b41d7c99bc20317bc470d22ae9f744
|
7
|
+
data.tar.gz: 0120c81aa41cbc7a32d039fb4c645b2fa1ca510d91093c813e4e3b8802e69393e2641af63c99287fbb8b75f56021461e1c2eaf67351874f7ee8865195af7cac6
|
data/README.md
CHANGED
@@ -138,7 +138,7 @@ module MyAPI
|
|
138
138
|
|
139
139
|
# Execute action, return the list
|
140
140
|
def exec
|
141
|
-
query
|
141
|
+
with_pagination(query)
|
142
142
|
end
|
143
143
|
end
|
144
144
|
|
@@ -178,9 +178,9 @@ module MyAPI
|
|
178
178
|
user = ::User.new(input)
|
179
179
|
|
180
180
|
if user.save
|
181
|
-
ok(user)
|
181
|
+
ok!(user)
|
182
182
|
else
|
183
|
-
error('save failed', user.errors.to_hash)
|
183
|
+
error!('save failed', user.errors.to_hash)
|
184
184
|
end
|
185
185
|
end
|
186
186
|
end
|
data/haveapi.gemspec
CHANGED
@@ -16,7 +16,7 @@ Gem::Specification.new do |s|
|
|
16
16
|
|
17
17
|
s.add_dependency 'activesupport', '>= 7.1'
|
18
18
|
s.add_dependency 'github-markdown'
|
19
|
-
s.add_dependency 'haveapi-client', '~> 0.
|
19
|
+
s.add_dependency 'haveapi-client', '~> 0.25.0'
|
20
20
|
s.add_dependency 'json'
|
21
21
|
s.add_dependency 'mail'
|
22
22
|
s.add_dependency 'nesty', '~> 1.0'
|
@@ -24,7 +24,7 @@ Gem::Specification.new do |s|
|
|
24
24
|
s.add_dependency 'rake'
|
25
25
|
s.add_dependency 'redcarpet', '~> 3.6'
|
26
26
|
s.add_dependency 'require_all', '~> 2.0.0'
|
27
|
-
s.add_dependency 'sinatra', '~>
|
28
|
-
s.add_dependency 'sinatra-contrib', '~>
|
29
|
-
s.add_dependency 'tilt', '~> 2.
|
27
|
+
s.add_dependency 'sinatra', '~> 4.0'
|
28
|
+
s.add_dependency 'sinatra-contrib', '~> 4.0'
|
29
|
+
s.add_dependency 'tilt', '~> 2.4'
|
30
30
|
end
|
data/lib/haveapi/action.rb
CHANGED
@@ -323,7 +323,7 @@ module HaveAPI
|
|
323
323
|
def validate!
|
324
324
|
@params = validate
|
325
325
|
rescue ValidationError => e
|
326
|
-
error(e.message, e.to_hash)
|
326
|
+
error!(e.message, e.to_hash)
|
327
327
|
end
|
328
328
|
|
329
329
|
def authorized?(user)
|
@@ -379,11 +379,11 @@ module HaveAPI
|
|
379
379
|
if tmp.empty?
|
380
380
|
p e.message
|
381
381
|
puts e.backtrace
|
382
|
-
error('Server error occurred')
|
382
|
+
error!('Server error occurred')
|
383
383
|
end
|
384
384
|
|
385
385
|
unless tmp[:status]
|
386
|
-
error(tmp[:message], {}, http_status: tmp[:http_status] || 500)
|
386
|
+
error!(tmp[:message], {}, http_status: tmp[:http_status] || 500)
|
387
387
|
end
|
388
388
|
end
|
389
389
|
|
@@ -555,7 +555,7 @@ module HaveAPI
|
|
555
555
|
# @param ret [Hash] response
|
556
556
|
# @param opts [Hash] options
|
557
557
|
# @option opts [Integer] http_status HTTP status code sent to the client
|
558
|
-
def ok(ret = {}, opts = {})
|
558
|
+
def ok!(ret = {}, opts = {})
|
559
559
|
@http_status = opts[:http_status]
|
560
560
|
throw(:return, ret)
|
561
561
|
end
|
@@ -564,7 +564,7 @@ module HaveAPI
|
|
564
564
|
# @param errs [Hash<Array>] parameter errors sent to the client
|
565
565
|
# @param opts [Hash] options
|
566
566
|
# @option opts [Integer] http_status HTTP status code sent to the client
|
567
|
-
def error(msg, errs = {}, opts = {})
|
567
|
+
def error!(msg, errs = {}, opts = {})
|
568
568
|
@message = msg
|
569
569
|
@errors = errs
|
570
570
|
@http_status = opts[:http_status]
|
data/lib/haveapi/action_state.rb
CHANGED
@@ -11,11 +11,11 @@ module HaveAPI
|
|
11
11
|
class ActionState
|
12
12
|
# Return an array of objects representing actions that are pending completion.
|
13
13
|
# @param [Object] user
|
14
|
-
# @param [Integer]
|
14
|
+
# @param [Integer] from_id
|
15
15
|
# @param [Integer] limit
|
16
16
|
# @param [Symbol] order (:newest or :oldest)
|
17
17
|
# @return [Array<ActionState>]
|
18
|
-
def self.list_pending(user,
|
18
|
+
def self.list_pending(user, from_id, limit, order)
|
19
19
|
raise NotImplementedError
|
20
20
|
end
|
21
21
|
|
@@ -2,9 +2,9 @@ module HaveAPI::Actions
|
|
2
2
|
module Paginable
|
3
3
|
def self.included(action)
|
4
4
|
action.input do
|
5
|
-
integer :
|
6
|
-
|
7
|
-
integer :limit, label: 'Limit', desc: '
|
5
|
+
integer :from_id, label: 'From ID', desc: 'List objects with greater/lesser ID',
|
6
|
+
number: { min: 0 }
|
7
|
+
integer :limit, label: 'Limit', desc: 'Number of objects to retrieve',
|
8
8
|
number: { min: 0 }
|
9
9
|
end
|
10
10
|
end
|
@@ -159,7 +159,7 @@ module HaveAPI::Authentication
|
|
159
159
|
# @param request [Sinatra::Request]
|
160
160
|
# @return [String]
|
161
161
|
def token(request)
|
162
|
-
request[config.class.query_parameter] || request.env[header_to_env]
|
162
|
+
request.params[config.class.query_parameter] || request.env[header_to_env]
|
163
163
|
end
|
164
164
|
|
165
165
|
def describe
|
@@ -227,11 +227,11 @@ module HaveAPI::Authentication
|
|
227
227
|
input:
|
228
228
|
), ActionResult.new)
|
229
229
|
rescue HaveAPI::AuthenticationError => e
|
230
|
-
error(e.message)
|
230
|
+
error!(e.message)
|
231
231
|
end
|
232
232
|
|
233
233
|
unless result.ok?
|
234
|
-
error(result.error || 'invalid authentication credentials')
|
234
|
+
error!(result.error || 'invalid authentication credentials')
|
235
235
|
end
|
236
236
|
|
237
237
|
{
|
@@ -260,9 +260,9 @@ module HaveAPI::Authentication
|
|
260
260
|
), ActionResult.new)
|
261
261
|
|
262
262
|
if result.ok?
|
263
|
-
ok
|
263
|
+
ok!
|
264
264
|
else
|
265
|
-
error(result.error || 'revoke failed')
|
265
|
+
error!(result.error || 'revoke failed')
|
266
266
|
end
|
267
267
|
end
|
268
268
|
end
|
@@ -290,7 +290,7 @@ module HaveAPI::Authentication
|
|
290
290
|
if result.ok?
|
291
291
|
{ valid_to: result.valid_to }
|
292
292
|
else
|
293
|
-
error(result.error || 'renew failed')
|
293
|
+
error!(result.error || 'renew failed')
|
294
294
|
end
|
295
295
|
end
|
296
296
|
end
|
@@ -324,11 +324,11 @@ module HaveAPI::Authentication
|
|
324
324
|
token: input[:token]
|
325
325
|
), ActionResult.new)
|
326
326
|
rescue HaveAPI::AuthenticationError => e
|
327
|
-
error(e.message)
|
327
|
+
error!(e.message)
|
328
328
|
end
|
329
329
|
|
330
330
|
unless result.ok?
|
331
|
-
error(result.error || 'authentication failed')
|
331
|
+
error!(result.error || 'authentication failed')
|
332
332
|
end
|
333
333
|
|
334
334
|
{
|
@@ -41,6 +41,47 @@ module HaveAPI::ModelAdapters
|
|
41
41
|
end
|
42
42
|
end
|
43
43
|
|
44
|
+
# Apply pagination on query in ascending order
|
45
|
+
# @param q [ActiveRecord::Relation] query to apply pagination on
|
46
|
+
def with_asc_pagination(q = nil)
|
47
|
+
ar_with_pagination(q, check: true) do |query, from_id|
|
48
|
+
query.where("`#{self.class.model.table_name}`.`#{self.class.model.primary_key}` > ?", from_id)
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
# Apply pagination on query in descending order
|
53
|
+
# @param q [ActiveRecord::Relation] query to apply pagination on
|
54
|
+
def with_desc_pagination(q = nil)
|
55
|
+
ar_with_pagination(q, check: true) do |query, from_id|
|
56
|
+
query.where("`#{self.class.model.table_name}`.`#{self.class.model.primary_key}` < ?", from_id)
|
57
|
+
end
|
58
|
+
end
|
59
|
+
|
60
|
+
alias with_pagination with_asc_pagination
|
61
|
+
|
62
|
+
# @param q [ActiveRecord::Relation] query to apply pagination on
|
63
|
+
# @param parameter [Symbol] input parameter used for pagination
|
64
|
+
# @param check [Boolean] raise if the model does not have a simple primary key
|
65
|
+
# @yieldparam q [ActiveRecord::Relation] query to apply pagination on
|
66
|
+
# @yieldparam parameter [any] value of the input parameter
|
67
|
+
def ar_with_pagination(q, parameter: :from_id, check: false)
|
68
|
+
pk = self.class.model.primary_key
|
69
|
+
|
70
|
+
if check && !pk.is_a?(String)
|
71
|
+
raise 'only simple primary key is supported, ' \
|
72
|
+
"#{self.class.model} has a composite primary key (#{pk.join(', ')})"
|
73
|
+
end
|
74
|
+
|
75
|
+
q ||= self.class.model.all
|
76
|
+
|
77
|
+
paginable = input[parameter]
|
78
|
+
limit = input[:limit]
|
79
|
+
|
80
|
+
q = yield(q, paginable) if paginable
|
81
|
+
q = q.limit(limit) if limit
|
82
|
+
q
|
83
|
+
end
|
84
|
+
|
44
85
|
# Parse includes sent by the user and return them
|
45
86
|
# in an array of symbols and hashes.
|
46
87
|
def ar_parse_includes(raw)
|
data/lib/haveapi/params.rb
CHANGED
@@ -154,6 +154,13 @@ module HaveAPI
|
|
154
154
|
@params.detect { |p| p.name == name }.patch(changes)
|
155
155
|
end
|
156
156
|
|
157
|
+
def remove(name)
|
158
|
+
i = @params.index { |p| p.name == name }
|
159
|
+
raise "Parameter #{name.inspect} not found" if i.nil?
|
160
|
+
|
161
|
+
@params.delete_at(i)
|
162
|
+
end
|
163
|
+
|
157
164
|
# Action returns custom data.
|
158
165
|
def custom(name, **kwargs, &block)
|
159
166
|
add_param(name, apply(kwargs, type: Custom, clean: block))
|
@@ -63,7 +63,7 @@ module HaveAPI::Resources
|
|
63
63
|
def exec
|
64
64
|
actions = @context.server.action_state.list_pending(
|
65
65
|
current_user,
|
66
|
-
input[:
|
66
|
+
input[:from_id],
|
67
67
|
input[:limit],
|
68
68
|
input[:order].to_sym
|
69
69
|
)
|
@@ -106,7 +106,7 @@ module HaveAPI::Resources
|
|
106
106
|
id: params[:action_state_id]
|
107
107
|
)
|
108
108
|
|
109
|
-
error('action state not found') unless state.valid?
|
109
|
+
error!('action state not found') unless state.valid?
|
110
110
|
|
111
111
|
if state.finished? || (Time.now - t) >= input[:timeout]
|
112
112
|
return state_to_hash(state)
|
@@ -145,7 +145,7 @@ module HaveAPI::Resources
|
|
145
145
|
|
146
146
|
return state_to_hash(state) if state.valid?
|
147
147
|
|
148
|
-
error('action state not found')
|
148
|
+
error!('action state not found')
|
149
149
|
end
|
150
150
|
end
|
151
151
|
|
@@ -164,7 +164,7 @@ module HaveAPI::Resources
|
|
164
164
|
id: params[:action_state_id]
|
165
165
|
)
|
166
166
|
|
167
|
-
error('action state not found') unless state.valid?
|
167
|
+
error!('action state not found') unless state.valid?
|
168
168
|
|
169
169
|
ret = state.cancel
|
170
170
|
|
@@ -172,13 +172,13 @@ module HaveAPI::Resources
|
|
172
172
|
@state_id = ret
|
173
173
|
|
174
174
|
elsif ret
|
175
|
-
ok
|
175
|
+
ok!
|
176
176
|
|
177
177
|
else
|
178
|
-
error('cancellation failed')
|
178
|
+
error!('cancellation failed')
|
179
179
|
end
|
180
180
|
rescue RuntimeError, NotImplementedError => e
|
181
|
-
error(e.message)
|
181
|
+
error!(e.message)
|
182
182
|
end
|
183
183
|
|
184
184
|
attr_reader :state_id
|
data/lib/haveapi/server.rb
CHANGED
@@ -74,11 +74,11 @@ module HaveAPI
|
|
74
74
|
return unless request.env['HTTP_ORIGIN'] && request.env['HTTP_ACCESS_CONTROL_REQUEST_METHOD']
|
75
75
|
|
76
76
|
halt 200, {
|
77
|
-
'
|
78
|
-
'
|
79
|
-
'
|
80
|
-
'
|
81
|
-
'
|
77
|
+
'access-control-allow-origin' => '*',
|
78
|
+
'access-control-allow-methods' => 'GET,POST,OPTIONS,PATCH,PUT,DELETE',
|
79
|
+
'access-control-allow-credentials' => 'false',
|
80
|
+
'access-control-allow-headers' => settings.api_server.allowed_headers,
|
81
|
+
'access-control-max-age' => (60 * 60).to_s
|
82
82
|
}, ''
|
83
83
|
end
|
84
84
|
|
@@ -94,7 +94,7 @@ module HaveAPI
|
|
94
94
|
def require_auth!
|
95
95
|
report_error(
|
96
96
|
401,
|
97
|
-
{ '
|
97
|
+
{ 'www-authenticate' => 'Basic realm="Restricted Area"' },
|
98
98
|
'Action requires user to authenticate'
|
99
99
|
)
|
100
100
|
end
|
@@ -166,13 +166,19 @@ module HaveAPI
|
|
166
166
|
ret += "<h5>#{name.to_s.capitalize}</h5>"
|
167
167
|
ret += '<dl>'
|
168
168
|
opts.each do |k, v|
|
169
|
-
ret += "<dt>#{k}</dt><dd>#{v}</dd>"
|
169
|
+
ret += "<dt>#{k}</dt><dd>#{escape_html(v.to_s)}</dd>"
|
170
170
|
end
|
171
171
|
ret += '</dl>'
|
172
172
|
end
|
173
173
|
|
174
174
|
ret
|
175
175
|
end
|
176
|
+
|
177
|
+
def escape_html(v)
|
178
|
+
return '' if v.nil?
|
179
|
+
|
180
|
+
CGI.escapeHTML(v.to_s)
|
181
|
+
end
|
176
182
|
end
|
177
183
|
|
178
184
|
def initialize(module_name = HaveAPI.module_name)
|
@@ -231,8 +237,8 @@ module HaveAPI
|
|
231
237
|
|
232
238
|
before do
|
233
239
|
if request.env['HTTP_ORIGIN']
|
234
|
-
headers '
|
235
|
-
'
|
240
|
+
headers 'access-control-allow-origin' => '*',
|
241
|
+
'access-control-allow-credentials' => 'false'
|
236
242
|
end
|
237
243
|
end
|
238
244
|
|
@@ -468,8 +474,6 @@ module HaveAPI
|
|
468
474
|
authenticated?(v)
|
469
475
|
end
|
470
476
|
|
471
|
-
request.body.rewind
|
472
|
-
|
473
477
|
begin
|
474
478
|
body = request.body.read
|
475
479
|
|
@@ -42,7 +42,7 @@ module HaveAPI::Spec
|
|
42
42
|
method(action.http_method).call(
|
43
43
|
path,
|
44
44
|
params && params.to_json,
|
45
|
-
{ '
|
45
|
+
{ 'content-type' => 'application/json' }
|
46
46
|
)
|
47
47
|
|
48
48
|
else
|
@@ -51,7 +51,7 @@ module HaveAPI::Spec
|
|
51
51
|
method(http_method).call(
|
52
52
|
path,
|
53
53
|
params && params.to_json,
|
54
|
-
{ '
|
54
|
+
{ 'content-type' => 'application/json' }
|
55
55
|
)
|
56
56
|
end
|
57
57
|
end
|
data/lib/haveapi/version.rb
CHANGED
@@ -54,8 +54,8 @@
|
|
54
54
|
<td><%= info[:required] ? 'yes' : 'no' %></td>
|
55
55
|
<td><%= format_param_type(info) %></td>
|
56
56
|
<td><%= format_validators(info[:validators]) %></td>
|
57
|
-
<td><%= info[:default] == :_nil ? '' : info[:default] %></td>
|
58
|
-
<td><%= info[:description] %></td>
|
57
|
+
<td><%= info[:default] == :_nil ? '' : escape_html(info[:default]) %></td>
|
58
|
+
<td><%= escape_html(info[:description]) %></td>
|
59
59
|
</tr>
|
60
60
|
<% end %>
|
61
61
|
</table>
|
@@ -86,18 +86,13 @@
|
|
86
86
|
<td><%= info[:label] %></td>
|
87
87
|
<td><%= param %></td>
|
88
88
|
<td><%= format_param_type(info) %></td>
|
89
|
-
<td><%= info[:description] %></td>
|
89
|
+
<td><%= escape_html(info[:description]) %></td>
|
90
90
|
</tr>
|
91
91
|
<% end %>
|
92
92
|
</table>
|
93
93
|
<% end %>
|
94
94
|
</div>
|
95
95
|
|
96
|
-
<div class="action-self-description">
|
97
|
-
<h4>Self-description</h4>
|
98
|
-
<pre><code><%= JSON.pretty_generate(info) %></code></pre>
|
99
|
-
</div>
|
100
|
-
|
101
96
|
<% unless info[:examples].empty? %>
|
102
97
|
<h4>Examples</h4>
|
103
98
|
<% info[:examples].each_with_index do |example, i| %>
|
@@ -111,33 +111,5 @@ nojsTabs({
|
|
111
111
|
|
112
112
|
event.preventDefault();
|
113
113
|
});
|
114
|
-
|
115
|
-
// Show/hide button for help messages
|
116
|
-
$('.action .action-self-description').each(function(i, el){
|
117
|
-
// replace text by show/hide button
|
118
|
-
var div = $(el);
|
119
|
-
var pre = div.find('pre');
|
120
|
-
var h4 = div.find('h4');
|
121
|
-
h4.text('');
|
122
|
-
|
123
|
-
h4.append(
|
124
|
-
$('<button>')
|
125
|
-
.attr('type', 'button')
|
126
|
-
.addClass('btn btn-primary btn-sm')
|
127
|
-
.text('Show self-description')
|
128
|
-
.click(function(event) {
|
129
|
-
var button = $(this);
|
130
|
-
button.toggleClass('active');
|
131
|
-
pre.toggle('fast');
|
132
|
-
|
133
|
-
if(button.hasClass('active'))
|
134
|
-
button.text('Hide self-description');
|
135
|
-
else
|
136
|
-
button.text('Show self-description');
|
137
|
-
})
|
138
|
-
);
|
139
|
-
|
140
|
-
$(el).find('pre').hide();
|
141
|
-
});
|
142
114
|
});
|
143
115
|
</script>
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: haveapi
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.25.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Jakub Skokan
|
@@ -44,14 +44,14 @@ dependencies:
|
|
44
44
|
requirements:
|
45
45
|
- - "~>"
|
46
46
|
- !ruby/object:Gem::Version
|
47
|
-
version: 0.
|
47
|
+
version: 0.25.0
|
48
48
|
type: :runtime
|
49
49
|
prerelease: false
|
50
50
|
version_requirements: !ruby/object:Gem::Requirement
|
51
51
|
requirements:
|
52
52
|
- - "~>"
|
53
53
|
- !ruby/object:Gem::Version
|
54
|
-
version: 0.
|
54
|
+
version: 0.25.0
|
55
55
|
- !ruby/object:Gem::Dependency
|
56
56
|
name: json
|
57
57
|
requirement: !ruby/object:Gem::Requirement
|
@@ -156,42 +156,42 @@ dependencies:
|
|
156
156
|
requirements:
|
157
157
|
- - "~>"
|
158
158
|
- !ruby/object:Gem::Version
|
159
|
-
version:
|
159
|
+
version: '4.0'
|
160
160
|
type: :runtime
|
161
161
|
prerelease: false
|
162
162
|
version_requirements: !ruby/object:Gem::Requirement
|
163
163
|
requirements:
|
164
164
|
- - "~>"
|
165
165
|
- !ruby/object:Gem::Version
|
166
|
-
version:
|
166
|
+
version: '4.0'
|
167
167
|
- !ruby/object:Gem::Dependency
|
168
168
|
name: sinatra-contrib
|
169
169
|
requirement: !ruby/object:Gem::Requirement
|
170
170
|
requirements:
|
171
171
|
- - "~>"
|
172
172
|
- !ruby/object:Gem::Version
|
173
|
-
version:
|
173
|
+
version: '4.0'
|
174
174
|
type: :runtime
|
175
175
|
prerelease: false
|
176
176
|
version_requirements: !ruby/object:Gem::Requirement
|
177
177
|
requirements:
|
178
178
|
- - "~>"
|
179
179
|
- !ruby/object:Gem::Version
|
180
|
-
version:
|
180
|
+
version: '4.0'
|
181
181
|
- !ruby/object:Gem::Dependency
|
182
182
|
name: tilt
|
183
183
|
requirement: !ruby/object:Gem::Requirement
|
184
184
|
requirements:
|
185
185
|
- - "~>"
|
186
186
|
- !ruby/object:Gem::Version
|
187
|
-
version: 2.
|
187
|
+
version: '2.4'
|
188
188
|
type: :runtime
|
189
189
|
prerelease: false
|
190
190
|
version_requirements: !ruby/object:Gem::Requirement
|
191
191
|
requirements:
|
192
192
|
- - "~>"
|
193
193
|
- !ruby/object:Gem::Version
|
194
|
-
version: 2.
|
194
|
+
version: '2.4'
|
195
195
|
description: Framework for creating self-describing APIs
|
196
196
|
email: jakub.skokan@vpsfree.cz
|
197
197
|
executables: []
|