haveapi 0.20.0 → 0.21.1

Sign up to get free protection for your applications and to get access to all the features.
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 +153 -167
  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 +10 -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