haveapi 0.19.3 → 0.21.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 +4 -4
- data/Gemfile +1 -1
- data/Rakefile +6 -6
- data/haveapi.gemspec +13 -13
- data/lib/haveapi/action.rb +140 -158
- data/lib/haveapi/action_state.rb +2 -6
- data/lib/haveapi/actions/default.rb +8 -10
- data/lib/haveapi/api.rb +2 -1
- data/lib/haveapi/authentication/base.rb +5 -8
- data/lib/haveapi/authentication/basic/provider.rb +4 -5
- data/lib/haveapi/authentication/chain.rb +19 -17
- data/lib/haveapi/authentication/oauth2/config.rb +12 -32
- data/lib/haveapi/authentication/oauth2/provider.rb +20 -30
- data/lib/haveapi/authentication/oauth2/revoke_endpoint.rb +1 -2
- data/lib/haveapi/authentication/token/action_config.rb +5 -3
- data/lib/haveapi/authentication/token/config.rb +5 -5
- data/lib/haveapi/authentication/token/provider.rb +33 -37
- data/lib/haveapi/authorization.rb +5 -4
- data/lib/haveapi/client_example.rb +11 -14
- data/lib/haveapi/client_examples/curl.rb +37 -37
- data/lib/haveapi/client_examples/fs_client.rb +29 -31
- data/lib/haveapi/client_examples/http.rb +35 -36
- data/lib/haveapi/client_examples/js_client.rb +62 -63
- data/lib/haveapi/client_examples/php_client.rb +77 -76
- data/lib/haveapi/client_examples/ruby_cli.rb +30 -30
- data/lib/haveapi/client_examples/ruby_client.rb +26 -26
- data/lib/haveapi/common.rb +3 -4
- data/lib/haveapi/context.rb +11 -10
- data/lib/haveapi/example.rb +9 -4
- data/lib/haveapi/example_list.rb +2 -2
- data/lib/haveapi/exceptions.rb +1 -1
- data/lib/haveapi/extensions/action_exceptions.rb +2 -2
- data/lib/haveapi/extensions/base.rb +1 -3
- data/lib/haveapi/extensions/exception_mailer.rb +260 -257
- data/lib/haveapi/hooks.rb +40 -39
- data/lib/haveapi/metadata.rb +1 -1
- data/lib/haveapi/model_adapter.rb +16 -27
- data/lib/haveapi/model_adapters/active_record.rb +59 -69
- data/lib/haveapi/output_formatter.rb +7 -7
- data/lib/haveapi/output_formatters/base.rb +2 -4
- data/lib/haveapi/parameters/resource.rb +7 -7
- data/lib/haveapi/parameters/typed.rb +6 -9
- data/lib/haveapi/params.rb +38 -45
- data/lib/haveapi/resource.rb +8 -8
- data/lib/haveapi/resources/action_state.rb +11 -19
- data/lib/haveapi/server.rb +105 -108
- data/lib/haveapi/spec/api_response.rb +1 -1
- data/lib/haveapi/spec/helpers.rb +1 -1
- data/lib/haveapi/spec/mock_action.rb +11 -10
- data/lib/haveapi/spec/spec_methods.rb +9 -8
- data/lib/haveapi/tasks/yard.rb +2 -2
- data/lib/haveapi/types.rb +5 -6
- data/lib/haveapi/validator.rb +6 -3
- data/lib/haveapi/validator_chain.rb +9 -8
- data/lib/haveapi/validators/acceptance.rb +6 -6
- data/lib/haveapi/validators/confirmation.rb +2 -3
- data/lib/haveapi/validators/exclusion.rb +1 -1
- data/lib/haveapi/validators/format.rb +1 -1
- data/lib/haveapi/validators/inclusion.rb +1 -1
- data/lib/haveapi/validators/length.rb +12 -11
- data/lib/haveapi/validators/numericality.rb +14 -13
- data/lib/haveapi/validators/presence.rb +4 -3
- data/lib/haveapi/version.rb +2 -2
- data/lib/haveapi.rb +2 -3
- data/spec/.rubocop.yml +4 -0
- data/spec/action/dsl_spec.rb +18 -18
- data/spec/authorization_spec.rb +8 -8
- data/spec/common_spec.rb +2 -1
- data/spec/documentation_spec.rb +2 -9
- data/spec/envelope_spec.rb +2 -2
- data/spec/hooks_spec.rb +12 -12
- data/spec/parameters/typed_spec.rb +6 -6
- data/spec/params_spec.rb +22 -24
- data/spec/resource_spec.rb +5 -7
- data/spec/spec_helper.rb +0 -1
- data/spec/validators/acceptance_spec.rb +1 -1
- data/spec/validators/confirmation_spec.rb +5 -5
- data/spec/validators/exclusion_spec.rb +3 -3
- data/spec/validators/format_spec.rb +2 -2
- data/spec/validators/inclusion_spec.rb +4 -4
- data/spec/validators/length_spec.rb +23 -23
- data/spec/validators/numericality_spec.rb +13 -13
- data/spec/validators/presence_spec.rb +3 -3
- metadata +49 -48
data/lib/haveapi/server.rb
CHANGED
@@ -6,34 +6,34 @@ require 'haveapi/hooks'
|
|
6
6
|
|
7
7
|
module HaveAPI
|
8
8
|
class Server
|
9
|
-
|
10
|
-
|
11
|
-
attr_accessor :action_state
|
9
|
+
attr_accessor :default_version, :action_state
|
10
|
+
attr_reader :root, :routes, :module_name, :auth_chain, :versions, :extensions
|
12
11
|
|
13
12
|
include Hookable
|
14
13
|
|
15
14
|
# Called after the user was authenticated (or not). The block is passed
|
16
15
|
# current user object or nil as an argument.
|
17
16
|
has_hook :post_authenticated,
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
17
|
+
desc: 'Called after the user was authenticated',
|
18
|
+
args: {
|
19
|
+
current_user: 'object returned by the authentication backend'
|
20
|
+
}
|
22
21
|
|
23
22
|
has_hook :description_exception,
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
23
|
+
desc: 'Called when an exception occurs when building self-description',
|
24
|
+
args: {
|
25
|
+
context: 'HaveAPI::Context',
|
26
|
+
exception: 'exception instance'
|
27
|
+
},
|
28
|
+
ret: {
|
29
|
+
http_status: 'HTTP status code to send to client',
|
30
|
+
message: 'error message sent to the client'
|
31
|
+
}
|
33
32
|
|
34
33
|
module ServerHelpers
|
35
34
|
def setup_formatter
|
36
35
|
return if @formatter
|
36
|
+
|
37
37
|
@formatter = OutputFormatter.new
|
38
38
|
|
39
39
|
unless @formatter.supports?(request.accept)
|
@@ -57,15 +57,15 @@ module HaveAPI
|
|
57
57
|
end
|
58
58
|
|
59
59
|
def access_control
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
60
|
+
return unless request.env['HTTP_ORIGIN'] && request.env['HTTP_ACCESS_CONTROL_REQUEST_METHOD']
|
61
|
+
|
62
|
+
halt 200, {
|
63
|
+
'Access-Control-Allow-Origin' => '*',
|
64
|
+
'Access-Control-Allow-Methods' => 'GET,POST,OPTIONS,PATCH,PUT,DELETE',
|
65
|
+
'Access-Control-Allow-Credentials' => 'false',
|
66
|
+
'Access-Control-Allow-Headers' => settings.api_server.allowed_headers,
|
67
|
+
'Access-Control-Max-Age' => (60 * 60).to_s
|
68
|
+
}, ''
|
69
69
|
end
|
70
70
|
|
71
71
|
def current_user
|
@@ -80,7 +80,7 @@ module HaveAPI
|
|
80
80
|
def require_auth!
|
81
81
|
report_error(
|
82
82
|
401,
|
83
|
-
{'WWW-Authenticate' => 'Basic realm="Restricted Area"'},
|
83
|
+
{ 'WWW-Authenticate' => 'Basic realm="Restricted Area"' },
|
84
84
|
'Action requires user to authenticate'
|
85
85
|
)
|
86
86
|
end
|
@@ -106,12 +106,12 @@ module HaveAPI
|
|
106
106
|
end
|
107
107
|
|
108
108
|
def base_url
|
109
|
-
if request.env['HTTP_X_FORWARDED_SSL'] == 'on'
|
110
|
-
|
109
|
+
scheme = if request.env['HTTP_X_FORWARDED_SSL'] == 'on'
|
110
|
+
'https'
|
111
111
|
|
112
|
-
|
113
|
-
|
114
|
-
|
112
|
+
else
|
113
|
+
request.env['rack.url_scheme']
|
114
|
+
end
|
115
115
|
|
116
116
|
"#{scheme}://#{request.env['HTTP_HOST']}"
|
117
117
|
end
|
@@ -140,6 +140,7 @@ module HaveAPI
|
|
140
140
|
module DocHelpers
|
141
141
|
def format_param_type(param)
|
142
142
|
return param[:type] if param[:type] != 'Resource'
|
143
|
+
|
143
144
|
"<a href=\"#root-#{param[:resource].join('-')}-show\">#{param[:type]}</a>"
|
144
145
|
end
|
145
146
|
|
@@ -188,14 +189,11 @@ module HaveAPI
|
|
188
189
|
end
|
189
190
|
|
190
191
|
# Set default version of API.
|
191
|
-
def default_version=(v)
|
192
|
-
@default_version = v
|
193
|
-
end
|
194
192
|
|
195
193
|
# Load routes for all resource from included API versions.
|
196
194
|
# All routes are mounted under prefix `path`.
|
197
195
|
# If no default version is set, the last included version is used.
|
198
|
-
def mount(prefix='/')
|
196
|
+
def mount(prefix = '/')
|
199
197
|
@root = prefix
|
200
198
|
|
201
199
|
@sinatra = Sinatra.new do
|
@@ -203,8 +201,8 @@ module HaveAPI
|
|
203
201
|
# for markdown files with extension .md, only .markdown
|
204
202
|
Tilt[:md]
|
205
203
|
|
206
|
-
set :views, settings.root
|
207
|
-
set :public_folder, settings.root
|
204
|
+
set :views, "#{settings.root}/views"
|
205
|
+
set :public_folder, "#{settings.root}/public"
|
208
206
|
set :bind, '0.0.0.0'
|
209
207
|
|
210
208
|
if settings.development?
|
@@ -230,7 +228,9 @@ module HaveAPI
|
|
230
228
|
end
|
231
229
|
|
232
230
|
after do
|
233
|
-
|
231
|
+
if Object.const_defined?(:ActiveRecord)
|
232
|
+
ActiveRecord::Base.connection_handler.clear_active_connections!
|
233
|
+
end
|
234
234
|
end
|
235
235
|
end
|
236
236
|
|
@@ -244,10 +244,10 @@ module HaveAPI
|
|
244
244
|
authenticated?(settings.api_server.default_version)
|
245
245
|
|
246
246
|
@api = settings.api_server.describe(Context.new(
|
247
|
-
|
248
|
-
|
249
|
-
|
250
|
-
|
247
|
+
settings.api_server,
|
248
|
+
user: current_user,
|
249
|
+
params:
|
250
|
+
))
|
251
251
|
|
252
252
|
content_type 'text/html'
|
253
253
|
erb :index, layout: :main_layout
|
@@ -259,27 +259,27 @@ module HaveAPI
|
|
259
259
|
authenticated?(settings.api_server.default_version)
|
260
260
|
ret = nil
|
261
261
|
|
262
|
-
case params[:describe]
|
263
|
-
|
264
|
-
|
265
|
-
|
266
|
-
|
267
|
-
|
268
|
-
|
269
|
-
|
270
|
-
|
271
|
-
|
272
|
-
|
273
|
-
|
274
|
-
|
275
|
-
|
276
|
-
|
277
|
-
|
278
|
-
|
279
|
-
|
280
|
-
|
281
|
-
|
282
|
-
|
262
|
+
ret = case params[:describe]
|
263
|
+
when 'versions'
|
264
|
+
{
|
265
|
+
versions: settings.api_server.versions,
|
266
|
+
default: settings.api_server.default_version
|
267
|
+
}
|
268
|
+
|
269
|
+
when 'default'
|
270
|
+
settings.api_server.describe_version(Context.new(
|
271
|
+
settings.api_server,
|
272
|
+
version: settings.api_server.default_version,
|
273
|
+
user: current_user, params:
|
274
|
+
))
|
275
|
+
|
276
|
+
else
|
277
|
+
settings.api_server.describe(Context.new(
|
278
|
+
settings.api_server,
|
279
|
+
user: current_user,
|
280
|
+
params:
|
281
|
+
))
|
282
|
+
end
|
283
283
|
|
284
284
|
@formatter.format(true, ret)
|
285
285
|
end
|
@@ -296,7 +296,7 @@ module HaveAPI
|
|
296
296
|
content_type 'text/html'
|
297
297
|
|
298
298
|
erb :main_layout do
|
299
|
-
GitHub::Markdown.render(File.new(settings.views
|
299
|
+
GitHub::Markdown.render(File.new("#{settings.views}/../../../README.md").read)
|
300
300
|
end
|
301
301
|
end
|
302
302
|
|
@@ -313,7 +313,6 @@ module HaveAPI
|
|
313
313
|
erb :doc_layout, layout: :main_layout do
|
314
314
|
begin
|
315
315
|
@content = doc(f)
|
316
|
-
|
317
316
|
rescue Errno::ENOENT
|
318
317
|
halt 404
|
319
318
|
end
|
@@ -357,11 +356,11 @@ module HaveAPI
|
|
357
356
|
|
358
357
|
@v = v
|
359
358
|
@help = settings.api_server.describe_version(Context.new(
|
360
|
-
|
361
|
-
|
362
|
-
|
363
|
-
|
364
|
-
|
359
|
+
settings.api_server,
|
360
|
+
version: v,
|
361
|
+
user: current_user,
|
362
|
+
params:
|
363
|
+
))
|
365
364
|
|
366
365
|
content_type 'text/html'
|
367
366
|
erb :doc_layout, layout: :main_layout do
|
@@ -376,11 +375,11 @@ module HaveAPI
|
|
376
375
|
authenticated?(v)
|
377
376
|
|
378
377
|
@formatter.format(true, settings.api_server.describe_version(Context.new(
|
379
|
-
|
380
|
-
|
381
|
-
|
382
|
-
|
383
|
-
|
378
|
+
settings.api_server,
|
379
|
+
version: v,
|
380
|
+
user: current_user,
|
381
|
+
params:
|
382
|
+
)))
|
384
383
|
end
|
385
384
|
|
386
385
|
# Register blocking resource
|
@@ -402,16 +401,14 @@ module HaveAPI
|
|
402
401
|
|
403
402
|
def validate_resources(resources)
|
404
403
|
resources.each_value do |r|
|
405
|
-
r[:actions].each_key
|
406
|
-
a.validate_build
|
407
|
-
end
|
404
|
+
r[:actions].each_key(&:validate_build)
|
408
405
|
|
409
406
|
validate_resources(r[:resources])
|
410
407
|
end
|
411
408
|
end
|
412
409
|
|
413
410
|
def mount_resource(prefix, v, resource, hash)
|
414
|
-
hash[resource] = {resources: {}, actions: {}}
|
411
|
+
hash[resource] = { resources: {}, actions: {} }
|
415
412
|
|
416
413
|
resource.routes(prefix).each do |route|
|
417
414
|
if route.is_a?(Hash)
|
@@ -428,7 +425,7 @@ module HaveAPI
|
|
428
425
|
end
|
429
426
|
|
430
427
|
def mount_nested_resource(v, routes)
|
431
|
-
ret = {resources: {}, actions: {}}
|
428
|
+
ret = { resources: {}, actions: {} }
|
432
429
|
|
433
430
|
routes.each do |route|
|
434
431
|
if route.is_a?(Hash)
|
@@ -458,27 +455,26 @@ module HaveAPI
|
|
458
455
|
begin
|
459
456
|
body = request.body.read
|
460
457
|
|
461
|
-
if body.empty?
|
462
|
-
|
463
|
-
|
464
|
-
|
465
|
-
|
466
|
-
|
467
|
-
rescue => e
|
458
|
+
body = if body.empty?
|
459
|
+
nil
|
460
|
+
else
|
461
|
+
JSON.parse(body, symbolize_names: true)
|
462
|
+
end
|
463
|
+
rescue StandardError => e
|
468
464
|
report_error(400, {}, 'Bad JSON syntax')
|
469
465
|
end
|
470
466
|
|
471
467
|
action = route.action.new(request, v, params, body, Context.new(
|
472
|
-
|
473
|
-
|
474
|
-
|
475
|
-
|
476
|
-
|
477
|
-
|
478
|
-
|
479
|
-
|
480
|
-
|
481
|
-
|
468
|
+
settings.api_server,
|
469
|
+
version: v,
|
470
|
+
request: self,
|
471
|
+
action: route.action,
|
472
|
+
path: route.path,
|
473
|
+
params:,
|
474
|
+
user: current_user,
|
475
|
+
endpoint: true,
|
476
|
+
resource_path: route.resource_path
|
477
|
+
))
|
482
478
|
|
483
479
|
unless action.authorized?(current_user)
|
484
480
|
report_error(403, {}, 'Access denied. Insufficient permissions.')
|
@@ -491,11 +487,11 @@ module HaveAPI
|
|
491
487
|
http_status || 200,
|
492
488
|
@formatter.format(
|
493
489
|
status,
|
494
|
-
status
|
495
|
-
|
490
|
+
status ? reply : nil,
|
491
|
+
status ? nil : reply,
|
496
492
|
errors,
|
497
493
|
version: false
|
498
|
-
)
|
494
|
+
)
|
499
495
|
]
|
500
496
|
end
|
501
497
|
|
@@ -518,11 +514,11 @@ module HaveAPI
|
|
518
514
|
request: self,
|
519
515
|
action: route.action,
|
520
516
|
path: route.path,
|
521
|
-
args
|
522
|
-
params
|
517
|
+
args:,
|
518
|
+
params:,
|
523
519
|
user: current_user,
|
524
520
|
endpoint: true,
|
525
|
-
resource_path: route.resource_path
|
521
|
+
resource_path: route.resource_path
|
526
522
|
)
|
527
523
|
|
528
524
|
begin
|
@@ -531,8 +527,7 @@ module HaveAPI
|
|
531
527
|
unless desc
|
532
528
|
report_error(403, {}, 'Access denied. Insufficient permissions.')
|
533
529
|
end
|
534
|
-
|
535
|
-
rescue => e
|
530
|
+
rescue StandardError => e
|
536
531
|
tmp = settings.api_server.call_hooks_for(:description_exception, args: [ctx, e])
|
537
532
|
report_error(
|
538
533
|
tmp[:http_status] || 500,
|
@@ -550,7 +545,7 @@ module HaveAPI
|
|
550
545
|
|
551
546
|
ret = {
|
552
547
|
default_version: @default_version,
|
553
|
-
versions: {default: describe_version(context)}
|
548
|
+
versions: { default: describe_version(context) }
|
554
549
|
}
|
555
550
|
|
556
551
|
@versions.each do |v|
|
@@ -569,7 +564,7 @@ module HaveAPI
|
|
569
564
|
help: version_prefix(context.version)
|
570
565
|
}
|
571
566
|
|
572
|
-
#puts JSON.pretty_generate(@routes)
|
567
|
+
# puts JSON.pretty_generate(@routes)
|
573
568
|
|
574
569
|
@routes[context.version][:resources].each do |resource, children|
|
575
570
|
r_name = resource.resource_name.underscore
|
@@ -599,7 +594,7 @@ module HaveAPI
|
|
599
594
|
end
|
600
595
|
|
601
596
|
def add_auth_module(v, name, mod, prefix: '')
|
602
|
-
@routes[v] ||= {authentication: {name => {resources: {}}}}
|
597
|
+
@routes[v] ||= { authentication: { name => { resources: {} } } }
|
603
598
|
|
604
599
|
HaveAPI.get_version_resources(mod, v).each do |r|
|
605
600
|
mount_resource("#{@root}_auth/#{prefix}/", v, r, @routes[v][:authentication][name][:resources])
|
@@ -613,6 +608,7 @@ module HaveAPI
|
|
613
608
|
|
614
609
|
def allowed_headers
|
615
610
|
return @allowed_headers_str if @allowed_headers_str
|
611
|
+
|
616
612
|
@allowed_headers_str = @allowed_headers.join(',')
|
617
613
|
end
|
618
614
|
|
@@ -625,6 +621,7 @@ module HaveAPI
|
|
625
621
|
end
|
626
622
|
|
627
623
|
private
|
624
|
+
|
628
625
|
def do_authenticate(v, request)
|
629
626
|
@auth_chain.authenticate(v, request)
|
630
627
|
end
|
data/lib/haveapi/spec/helpers.rb
CHANGED
@@ -10,21 +10,22 @@ module HaveAPI::Spec
|
|
10
10
|
|
11
11
|
def call(input, user: nil, &block)
|
12
12
|
action = @action.new(nil, @v, input, nil, HaveAPI::Context.new(
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
13
|
+
@server,
|
14
|
+
version: @v,
|
15
|
+
action: @action,
|
16
|
+
path: @path,
|
17
|
+
params: input,
|
18
|
+
user:,
|
19
|
+
endpoint: true
|
20
|
+
))
|
21
21
|
|
22
22
|
unless action.authorized?(user)
|
23
|
-
|
23
|
+
raise 'Access denied. Insufficient permissions.'
|
24
24
|
end
|
25
25
|
|
26
26
|
status, data, errors = action.safe_exec
|
27
|
-
|
27
|
+
raise(data || 'action failed') unless status
|
28
|
+
|
28
29
|
action.instance_exec(@test, &block)
|
29
30
|
data
|
30
31
|
end
|
@@ -28,7 +28,7 @@ module HaveAPI::Spec
|
|
28
28
|
# Two modes:
|
29
29
|
# http_method, path, params = {}
|
30
30
|
# [resource], action, params, &block
|
31
|
-
def call_api(*args, &
|
31
|
+
def call_api(*args, &)
|
32
32
|
if args[0].is_a?(::Array) || args[1].is_a?(::Symbol)
|
33
33
|
r_name, a_name, params = args
|
34
34
|
|
@@ -42,7 +42,7 @@ module HaveAPI::Spec
|
|
42
42
|
method(action.http_method).call(
|
43
43
|
path,
|
44
44
|
params && params.to_json,
|
45
|
-
{'Content-Type' => 'application/json'}
|
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
|
-
{'Content-Type' => 'application/json'}
|
54
|
+
{ 'Content-Type' => 'application/json' }
|
55
55
|
)
|
56
56
|
end
|
57
57
|
end
|
@@ -79,22 +79,23 @@ module HaveAPI::Spec
|
|
79
79
|
v = version || @api.default_version
|
80
80
|
action, path = find_action(v, r_name, a_name)
|
81
81
|
m = MockAction.new(self, @api, action, path, v)
|
82
|
-
m.call(params, user
|
82
|
+
m.call(params, user:, &block)
|
83
83
|
end
|
84
84
|
|
85
85
|
# Return parsed API response.
|
86
86
|
# @return [HaveAPI::Spec::ApiResponse]
|
87
87
|
def api_response
|
88
|
-
if last_response
|
88
|
+
if last_response == @last_response
|
89
|
+
@api_response ||= ApiResponse.new(last_response.body)
|
90
|
+
else
|
89
91
|
@last_response = last_response
|
90
92
|
@api_response = ApiResponse.new(last_response.body)
|
91
93
|
|
92
|
-
else
|
93
|
-
@api_response ||= ApiResponse.new(last_response.body)
|
94
94
|
end
|
95
95
|
end
|
96
96
|
|
97
97
|
protected
|
98
|
+
|
98
99
|
def get_opt(name)
|
99
100
|
self.class.opts && self.class.opts[name]
|
100
101
|
end
|
@@ -113,7 +114,7 @@ module HaveAPI::Spec
|
|
113
114
|
end.second
|
114
115
|
end
|
115
116
|
|
116
|
-
top[:actions].detect do |k,
|
117
|
+
top[:actions].detect do |k, _v|
|
117
118
|
k.to_s.demodulize.underscore.to_sym == a_name
|
118
119
|
end
|
119
120
|
end
|
data/lib/haveapi/tasks/yard.rb
CHANGED
@@ -3,10 +3,10 @@ require 'haveapi/tasks/hooks'
|
|
3
3
|
def render_doc_file(src, dst)
|
4
4
|
src = File.join(File.dirname(__FILE__), '..', '..', '..', src)
|
5
5
|
|
6
|
-
|
6
|
+
proc do
|
7
7
|
File.write(
|
8
8
|
dst,
|
9
|
-
ERB.new(File.read(src)
|
9
|
+
ERB.new(File.read(src)).result(binding)
|
10
10
|
)
|
11
11
|
end
|
12
12
|
end
|
data/lib/haveapi/types.rb
CHANGED
@@ -2,23 +2,22 @@
|
|
2
2
|
module Boolean
|
3
3
|
def self.to_b(str)
|
4
4
|
return true if str === true
|
5
|
-
return true if str =~ /^(true|t|yes|y|1)$/i
|
6
|
-
|
7
5
|
return false if str === false
|
8
|
-
|
6
|
+
|
7
|
+
if str.respond_to?(:=~)
|
8
|
+
return true if str =~ /^(true|t|yes|y|1)$/i
|
9
|
+
return false if str =~ /^(false|f|no|n|0)$/i
|
10
|
+
end
|
9
11
|
|
10
12
|
false
|
11
13
|
end
|
12
14
|
end
|
13
15
|
|
14
16
|
module Datetime
|
15
|
-
|
16
17
|
end
|
17
18
|
|
18
19
|
module Custom
|
19
|
-
|
20
20
|
end
|
21
21
|
|
22
22
|
class Text < String
|
23
|
-
|
24
23
|
end
|
data/lib/haveapi/validator.rb
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
module HaveAPI
|
2
2
|
# Validators are stored in this module.
|
3
|
-
module Validators
|
3
|
+
module Validators; end
|
4
4
|
|
5
5
|
# Base class for all validators.
|
6
6
|
#
|
@@ -37,7 +37,7 @@ module HaveAPI
|
|
37
37
|
|
38
38
|
# True if this validator uses any of options in hash `opts`.
|
39
39
|
def use?(opts)
|
40
|
-
|
40
|
+
opts.keys.intersect?(@takes)
|
41
41
|
end
|
42
42
|
|
43
43
|
# Use the validator on given set of options in hash `opts`. Used
|
@@ -45,7 +45,8 @@ module HaveAPI
|
|
45
45
|
def use(opts)
|
46
46
|
keys = opts.keys & @takes
|
47
47
|
|
48
|
-
|
48
|
+
raise 'too many keys' if keys.size > 1
|
49
|
+
|
49
50
|
new(keys.first, opts.delete(keys.first))
|
50
51
|
end
|
51
52
|
end
|
@@ -94,6 +95,7 @@ module HaveAPI
|
|
94
95
|
end
|
95
96
|
|
96
97
|
protected
|
98
|
+
|
97
99
|
# This method has three modes of function.
|
98
100
|
#
|
99
101
|
# 1. If `v` is nil, it returns `@opts`. It is used if `@opts` is not a hash
|
@@ -109,6 +111,7 @@ module HaveAPI
|
|
109
111
|
else
|
110
112
|
return default unless @opts.is_a?(::Hash)
|
111
113
|
return @opts[v] unless @opts[v].nil?
|
114
|
+
|
112
115
|
default
|
113
116
|
end
|
114
117
|
end
|
@@ -8,6 +8,7 @@ module HaveAPI
|
|
8
8
|
find_validators(args) do |validator|
|
9
9
|
obj = validator.use(args)
|
10
10
|
next unless obj.useful?
|
11
|
+
|
11
12
|
@required = true if obj.is_a?(Validators::Presence)
|
12
13
|
@validators << obj
|
13
14
|
end
|
@@ -21,8 +22,8 @@ module HaveAPI
|
|
21
22
|
def add_or_replace(name, opt)
|
22
23
|
args = { name => opt }
|
23
24
|
|
24
|
-
unless v_class = find_validator(args)
|
25
|
-
|
25
|
+
unless (v_class = find_validator(args))
|
26
|
+
raise "validator for '#{name}' not found"
|
26
27
|
end
|
27
28
|
|
28
29
|
exists = @validators.detect { |v| v.is_a?(v_class) }
|
@@ -42,9 +43,9 @@ module HaveAPI
|
|
42
43
|
@validators << obj if obj.useful?
|
43
44
|
end
|
44
45
|
|
45
|
-
|
46
|
-
|
47
|
-
|
46
|
+
return unless v_class == Validators::Presence
|
47
|
+
|
48
|
+
@required = !opt.nil? && obj.useful?
|
48
49
|
end
|
49
50
|
|
50
51
|
# Returns true if validator Validators::Presence is used.
|
@@ -69,15 +70,15 @@ module HaveAPI
|
|
69
70
|
|
70
71
|
@validators.each do |validator|
|
71
72
|
next if validator.validate(value, params)
|
72
|
-
|
73
|
-
|
74
|
-
}
|
73
|
+
|
74
|
+
ret << (format(validator.message, value:))
|
75
75
|
end
|
76
76
|
|
77
77
|
ret.empty? ? true : ret
|
78
78
|
end
|
79
79
|
|
80
80
|
protected
|
81
|
+
|
81
82
|
def find_validator(args)
|
82
83
|
HaveAPI::Validators.constants.select do |v|
|
83
84
|
validator = HaveAPI::Validators.const_get(v)
|