haveapi 0.20.0 → 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.
Files changed (84) hide show
  1. checksums.yaml +4 -4
  2. data/Gemfile +1 -1
  3. data/Rakefile +6 -6
  4. data/haveapi.gemspec +13 -13
  5. data/lib/haveapi/action.rb +140 -158
  6. data/lib/haveapi/action_state.rb +2 -6
  7. data/lib/haveapi/actions/default.rb +8 -10
  8. data/lib/haveapi/api.rb +2 -1
  9. data/lib/haveapi/authentication/base.rb +5 -8
  10. data/lib/haveapi/authentication/basic/provider.rb +4 -5
  11. data/lib/haveapi/authentication/chain.rb +19 -17
  12. data/lib/haveapi/authentication/oauth2/config.rb +12 -32
  13. data/lib/haveapi/authentication/oauth2/provider.rb +20 -30
  14. data/lib/haveapi/authentication/oauth2/revoke_endpoint.rb +1 -2
  15. data/lib/haveapi/authentication/token/action_config.rb +5 -3
  16. data/lib/haveapi/authentication/token/config.rb +5 -5
  17. data/lib/haveapi/authentication/token/provider.rb +33 -37
  18. data/lib/haveapi/authorization.rb +5 -4
  19. data/lib/haveapi/client_example.rb +11 -14
  20. data/lib/haveapi/client_examples/curl.rb +37 -37
  21. data/lib/haveapi/client_examples/fs_client.rb +29 -31
  22. data/lib/haveapi/client_examples/http.rb +35 -36
  23. data/lib/haveapi/client_examples/js_client.rb +62 -63
  24. data/lib/haveapi/client_examples/php_client.rb +77 -76
  25. data/lib/haveapi/client_examples/ruby_cli.rb +30 -30
  26. data/lib/haveapi/client_examples/ruby_client.rb +26 -26
  27. data/lib/haveapi/common.rb +3 -4
  28. data/lib/haveapi/context.rb +11 -10
  29. data/lib/haveapi/example.rb +9 -4
  30. data/lib/haveapi/example_list.rb +2 -2
  31. data/lib/haveapi/exceptions.rb +1 -1
  32. data/lib/haveapi/extensions/action_exceptions.rb +2 -2
  33. data/lib/haveapi/extensions/base.rb +1 -3
  34. data/lib/haveapi/extensions/exception_mailer.rb +260 -257
  35. data/lib/haveapi/hooks.rb +40 -39
  36. data/lib/haveapi/metadata.rb +1 -1
  37. data/lib/haveapi/model_adapter.rb +16 -27
  38. data/lib/haveapi/model_adapters/active_record.rb +59 -69
  39. data/lib/haveapi/output_formatter.rb +7 -7
  40. data/lib/haveapi/output_formatters/base.rb +2 -4
  41. data/lib/haveapi/parameters/resource.rb +7 -7
  42. data/lib/haveapi/parameters/typed.rb +6 -9
  43. data/lib/haveapi/params.rb +38 -45
  44. data/lib/haveapi/resource.rb +8 -8
  45. data/lib/haveapi/resources/action_state.rb +11 -19
  46. data/lib/haveapi/server.rb +102 -107
  47. data/lib/haveapi/spec/api_response.rb +1 -1
  48. data/lib/haveapi/spec/helpers.rb +1 -1
  49. data/lib/haveapi/spec/mock_action.rb +11 -10
  50. data/lib/haveapi/spec/spec_methods.rb +9 -8
  51. data/lib/haveapi/tasks/yard.rb +2 -2
  52. data/lib/haveapi/types.rb +0 -3
  53. data/lib/haveapi/validator.rb +6 -3
  54. data/lib/haveapi/validator_chain.rb +9 -8
  55. data/lib/haveapi/validators/acceptance.rb +6 -6
  56. data/lib/haveapi/validators/confirmation.rb +2 -3
  57. data/lib/haveapi/validators/exclusion.rb +1 -1
  58. data/lib/haveapi/validators/format.rb +1 -1
  59. data/lib/haveapi/validators/inclusion.rb +1 -1
  60. data/lib/haveapi/validators/length.rb +12 -11
  61. data/lib/haveapi/validators/numericality.rb +14 -13
  62. data/lib/haveapi/validators/presence.rb +4 -3
  63. data/lib/haveapi/version.rb +2 -2
  64. data/lib/haveapi.rb +2 -3
  65. data/spec/.rubocop.yml +4 -0
  66. data/spec/action/dsl_spec.rb +18 -18
  67. data/spec/authorization_spec.rb +8 -8
  68. data/spec/common_spec.rb +2 -1
  69. data/spec/documentation_spec.rb +2 -9
  70. data/spec/envelope_spec.rb +2 -2
  71. data/spec/hooks_spec.rb +12 -12
  72. data/spec/parameters/typed_spec.rb +6 -6
  73. data/spec/params_spec.rb +22 -24
  74. data/spec/resource_spec.rb +5 -7
  75. data/spec/spec_helper.rb +0 -1
  76. data/spec/validators/acceptance_spec.rb +1 -1
  77. data/spec/validators/confirmation_spec.rb +5 -5
  78. data/spec/validators/exclusion_spec.rb +3 -3
  79. data/spec/validators/format_spec.rb +2 -2
  80. data/spec/validators/inclusion_spec.rb +4 -4
  81. data/spec/validators/length_spec.rb +23 -23
  82. data/spec/validators/numericality_spec.rb +13 -13
  83. data/spec/validators/presence_spec.rb +3 -3
  84. metadata +49 -48
@@ -6,34 +6,34 @@ require 'haveapi/hooks'
6
6
 
7
7
  module HaveAPI
8
8
  class Server
9
- attr_reader :root, :routes, :module_name, :auth_chain, :versions, :default_version,
10
- :extensions
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
- desc: 'Called after the user was authenticated',
19
- args: {
20
- current_user: 'object returned by the authentication backend',
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
- desc: 'Called when an exception occurs when building self-description',
25
- args: {
26
- context: 'HaveAPI::Context',
27
- exception: 'exception instance',
28
- },
29
- ret: {
30
- http_status: 'HTTP status code to send to client',
31
- message: 'error message sent to the client',
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
- if request.env['HTTP_ORIGIN'] && request.env['HTTP_ACCESS_CONTROL_REQUEST_METHOD']
61
- halt 200, {
62
- 'Access-Control-Allow-Origin' => '*',
63
- 'Access-Control-Allow-Methods' => 'GET,POST,OPTIONS,PATCH,PUT,DELETE',
64
- 'Access-Control-Allow-Credentials' => 'false',
65
- 'Access-Control-Allow-Headers' => settings.api_server.allowed_headers,
66
- 'Access-Control-Max-Age' => (60*60).to_s
67
- }, ''
68
- end
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
- scheme = 'https'
109
+ scheme = if request.env['HTTP_X_FORWARDED_SSL'] == 'on'
110
+ 'https'
111
111
 
112
- else
113
- scheme = request.env['rack.url_scheme']
114
- end
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 + '/views'
207
- set :public_folder, settings.root + '/public'
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?
@@ -246,10 +244,10 @@ module HaveAPI
246
244
  authenticated?(settings.api_server.default_version)
247
245
 
248
246
  @api = settings.api_server.describe(Context.new(
249
- settings.api_server,
250
- user: current_user,
251
- params: params
252
- ))
247
+ settings.api_server,
248
+ user: current_user,
249
+ params:
250
+ ))
253
251
 
254
252
  content_type 'text/html'
255
253
  erb :index, layout: :main_layout
@@ -261,27 +259,27 @@ module HaveAPI
261
259
  authenticated?(settings.api_server.default_version)
262
260
  ret = nil
263
261
 
264
- case params[:describe]
265
- when 'versions'
266
- ret = {
267
- versions: settings.api_server.versions,
268
- default: settings.api_server.default_version
269
- }
270
-
271
- when 'default'
272
- ret = settings.api_server.describe_version(Context.new(
273
- settings.api_server,
274
- version: settings.api_server.default_version,
275
- user: current_user, params: params
276
- ))
277
-
278
- else
279
- ret = settings.api_server.describe(Context.new(
280
- settings.api_server,
281
- user: current_user,
282
- params: params
283
- ))
284
- end
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
285
283
 
286
284
  @formatter.format(true, ret)
287
285
  end
@@ -298,7 +296,7 @@ module HaveAPI
298
296
  content_type 'text/html'
299
297
 
300
298
  erb :main_layout do
301
- GitHub::Markdown.render(File.new(settings.views + '/../../../README.md').read)
299
+ GitHub::Markdown.render(File.new("#{settings.views}/../../../README.md").read)
302
300
  end
303
301
  end
304
302
 
@@ -315,7 +313,6 @@ module HaveAPI
315
313
  erb :doc_layout, layout: :main_layout do
316
314
  begin
317
315
  @content = doc(f)
318
-
319
316
  rescue Errno::ENOENT
320
317
  halt 404
321
318
  end
@@ -359,11 +356,11 @@ module HaveAPI
359
356
 
360
357
  @v = v
361
358
  @help = settings.api_server.describe_version(Context.new(
362
- settings.api_server,
363
- version: v,
364
- user: current_user,
365
- params: params
366
- ))
359
+ settings.api_server,
360
+ version: v,
361
+ user: current_user,
362
+ params:
363
+ ))
367
364
 
368
365
  content_type 'text/html'
369
366
  erb :doc_layout, layout: :main_layout do
@@ -378,11 +375,11 @@ module HaveAPI
378
375
  authenticated?(v)
379
376
 
380
377
  @formatter.format(true, settings.api_server.describe_version(Context.new(
381
- settings.api_server,
382
- version: v,
383
- user: current_user,
384
- params: params
385
- )))
378
+ settings.api_server,
379
+ version: v,
380
+ user: current_user,
381
+ params:
382
+ )))
386
383
  end
387
384
 
388
385
  # Register blocking resource
@@ -404,16 +401,14 @@ module HaveAPI
404
401
 
405
402
  def validate_resources(resources)
406
403
  resources.each_value do |r|
407
- r[:actions].each_key do |a|
408
- a.validate_build
409
- end
404
+ r[:actions].each_key(&:validate_build)
410
405
 
411
406
  validate_resources(r[:resources])
412
407
  end
413
408
  end
414
409
 
415
410
  def mount_resource(prefix, v, resource, hash)
416
- hash[resource] = {resources: {}, actions: {}}
411
+ hash[resource] = { resources: {}, actions: {} }
417
412
 
418
413
  resource.routes(prefix).each do |route|
419
414
  if route.is_a?(Hash)
@@ -430,7 +425,7 @@ module HaveAPI
430
425
  end
431
426
 
432
427
  def mount_nested_resource(v, routes)
433
- ret = {resources: {}, actions: {}}
428
+ ret = { resources: {}, actions: {} }
434
429
 
435
430
  routes.each do |route|
436
431
  if route.is_a?(Hash)
@@ -460,27 +455,26 @@ module HaveAPI
460
455
  begin
461
456
  body = request.body.read
462
457
 
463
- if body.empty?
464
- body = nil
465
- else
466
- body = JSON.parse(body, symbolize_names: true)
467
- end
468
-
469
- rescue => e
458
+ body = if body.empty?
459
+ nil
460
+ else
461
+ JSON.parse(body, symbolize_names: true)
462
+ end
463
+ rescue StandardError => e
470
464
  report_error(400, {}, 'Bad JSON syntax')
471
465
  end
472
466
 
473
467
  action = route.action.new(request, v, params, body, Context.new(
474
- settings.api_server,
475
- version: v,
476
- request: self,
477
- action: route.action,
478
- path: route.path,
479
- params: params,
480
- user: current_user,
481
- endpoint: true,
482
- resource_path: route.resource_path,
483
- ))
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
+ ))
484
478
 
485
479
  unless action.authorized?(current_user)
486
480
  report_error(403, {}, 'Access denied. Insufficient permissions.')
@@ -493,11 +487,11 @@ module HaveAPI
493
487
  http_status || 200,
494
488
  @formatter.format(
495
489
  status,
496
- status ? reply : nil,
497
- !status ? reply : nil,
490
+ status ? reply : nil,
491
+ status ? nil : reply,
498
492
  errors,
499
493
  version: false
500
- ),
494
+ )
501
495
  ]
502
496
  end
503
497
 
@@ -520,11 +514,11 @@ module HaveAPI
520
514
  request: self,
521
515
  action: route.action,
522
516
  path: route.path,
523
- args: args,
524
- params: params,
517
+ args:,
518
+ params:,
525
519
  user: current_user,
526
520
  endpoint: true,
527
- resource_path: route.resource_path,
521
+ resource_path: route.resource_path
528
522
  )
529
523
 
530
524
  begin
@@ -533,8 +527,7 @@ module HaveAPI
533
527
  unless desc
534
528
  report_error(403, {}, 'Access denied. Insufficient permissions.')
535
529
  end
536
-
537
- rescue => e
530
+ rescue StandardError => e
538
531
  tmp = settings.api_server.call_hooks_for(:description_exception, args: [ctx, e])
539
532
  report_error(
540
533
  tmp[:http_status] || 500,
@@ -552,7 +545,7 @@ module HaveAPI
552
545
 
553
546
  ret = {
554
547
  default_version: @default_version,
555
- versions: {default: describe_version(context)},
548
+ versions: { default: describe_version(context) }
556
549
  }
557
550
 
558
551
  @versions.each do |v|
@@ -571,7 +564,7 @@ module HaveAPI
571
564
  help: version_prefix(context.version)
572
565
  }
573
566
 
574
- #puts JSON.pretty_generate(@routes)
567
+ # puts JSON.pretty_generate(@routes)
575
568
 
576
569
  @routes[context.version][:resources].each do |resource, children|
577
570
  r_name = resource.resource_name.underscore
@@ -601,7 +594,7 @@ module HaveAPI
601
594
  end
602
595
 
603
596
  def add_auth_module(v, name, mod, prefix: '')
604
- @routes[v] ||= {authentication: {name => {resources: {}}}}
597
+ @routes[v] ||= { authentication: { name => { resources: {} } } }
605
598
 
606
599
  HaveAPI.get_version_resources(mod, v).each do |r|
607
600
  mount_resource("#{@root}_auth/#{prefix}/", v, r, @routes[v][:authentication][name][:resources])
@@ -615,6 +608,7 @@ module HaveAPI
615
608
 
616
609
  def allowed_headers
617
610
  return @allowed_headers_str if @allowed_headers_str
611
+
618
612
  @allowed_headers_str = @allowed_headers.join(',')
619
613
  end
620
614
 
@@ -627,6 +621,7 @@ module HaveAPI
627
621
  end
628
622
 
629
623
  private
624
+
630
625
  def do_authenticate(v, request)
631
626
  @auth_chain.authenticate(v, request)
632
627
  end
@@ -1,4 +1,4 @@
1
- module HaveAPI::Spec
1
+ module HaveAPI::Spec
2
2
  # This class wraps raw reply from the API and provides a more friendly
3
3
  # interface.
4
4
  class ApiResponse
@@ -1,7 +1,7 @@
1
1
  require 'rack/test'
2
2
 
3
3
  module HaveAPI
4
- module Spec ; end
4
+ module Spec; end
5
5
  end
6
6
 
7
7
  require_relative 'api_builder'
@@ -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
- @server,
14
- version: @v,
15
- action: @action,
16
- path: @path,
17
- params: input,
18
- user: user,
19
- endpoint: true
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
- fail 'Access denied. Insufficient permissions.'
23
+ raise 'Access denied. Insufficient permissions.'
24
24
  end
25
25
 
26
26
  status, data, errors = action.safe_exec
27
- fail (data || 'action failed') unless status
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, &block)
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: user, &block)
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 != @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, v|
117
+ top[:actions].detect do |k, _v|
117
118
  k.to_s.demodulize.underscore.to_sym == a_name
118
119
  end
119
120
  end
@@ -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
- Proc.new do
6
+ proc do
7
7
  File.write(
8
8
  dst,
9
- ERB.new(File.read(src), 0).result(binding)
9
+ ERB.new(File.read(src)).result(binding)
10
10
  )
11
11
  end
12
12
  end
data/lib/haveapi/types.rb CHANGED
@@ -14,13 +14,10 @@ module Boolean
14
14
  end
15
15
 
16
16
  module Datetime
17
-
18
17
  end
19
18
 
20
19
  module Custom
21
-
22
20
  end
23
21
 
24
22
  class Text < String
25
-
26
23
  end
@@ -1,6 +1,6 @@
1
1
  module HaveAPI
2
2
  # Validators are stored in this module.
3
- module Validators ; end
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
- !(opts.keys & @takes).empty?
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
- fail 'too many keys' if keys.size > 1
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
- fail "validator for '#{name}' not found"
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
- if v_class == Validators::Presence
46
- @required = !opt.nil? && obj.useful? ? true : false
47
- end
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
- ret << validator.message % {
73
- value: value
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)
@@ -16,12 +16,12 @@ module HaveAPI
16
16
  takes :accept
17
17
 
18
18
  def setup
19
- if simple?
20
- @value = take
19
+ @value = if simple?
20
+ take
21
21
 
22
- else
23
- @value = take(:value)
24
- end
22
+ else
23
+ take(:value)
24
+ end
25
25
 
26
26
  @message = take(:message, "has to be #{@value}")
27
27
  end
@@ -29,7 +29,7 @@ module HaveAPI
29
29
  def describe
30
30
  {
31
31
  value: @value,
32
- message: @message,
32
+ message: @message
33
33
  }
34
34
  end
35
35
 
@@ -23,8 +23,7 @@ module HaveAPI
23
23
  @equal = take(:equal, true)
24
24
  @message = take(
25
25
  :message,
26
- @equal ? "must be the same as #{@param}"
27
- : "must be different from #{@param}"
26
+ @equal ? "must be the same as #{@param}" : "must be different from #{@param}"
28
27
  )
29
28
  end
30
29
 
@@ -32,7 +31,7 @@ module HaveAPI
32
31
  {
33
32
  equal: @equal ? true : false,
34
33
  parameter: @param,
35
- message: @message,
34
+ message: @message
36
35
  }
37
36
  end
38
37