api_hammer 0.12.0 → 0.13.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/CHANGELOG.md +7 -0
- data/lib/api_hammer/{halt.rb → halt_methods.rb} +49 -95
- data/lib/api_hammer/rails/halt.rb +47 -0
- data/lib/api_hammer/rails.rb +9 -7
- data/lib/api_hammer/sinatra/halt.rb +14 -0
- data/lib/api_hammer/sinatra.rb +157 -0
- data/lib/api_hammer/version.rb +1 -1
- data/lib/api_hammer.rb +1 -0
- data/test/check_required_params_test.rb +7 -7
- data/test/halt_test.rb +5 -5
- metadata +8 -5
- /data/lib/api_hammer/{check_required_params.rb → rails/check_required_params.rb} +0 -0
- /data/lib/api_hammer/{unmunged_request_params.rb → rails/unmunged_request_params.rb} +0 -0
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: a4967ef9a4193e2caca1f27035eb81f81e1ed918
|
4
|
+
data.tar.gz: 61f6a27ec7f13762954656f841666dddc37c9a8c
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: ea757b0f6203e080b27eee87ba0094db03a71bfc517f0c33223d37049bd2972ad2db6cf48601668bb2c20625628194bc5eb2897bfeb95d9df097fd2dae345cc7
|
7
|
+
data.tar.gz: 141e9a7c5aae36bedd0f1293c4faddae6621f6fa0d8b956ee44309a44628fad1245d012d9d9ad6c52ba233fca74465eb3b47187f479ce0d37583ca8e5b83138c
|
data/CHANGELOG.md
CHANGED
@@ -1,3 +1,10 @@
|
|
1
|
+
# v0.13.0
|
2
|
+
- ApiHammer::Sinatra, with some useful sinatra methods
|
3
|
+
- #halt, #halt_error, #halt_unprocessable_entity and friends
|
4
|
+
- a more api-appropriate 404 for unknown routes
|
5
|
+
- parsing request bodies in accordance with content-type, with appropriate errors
|
6
|
+
- formatting response bodies, minding accept headers, with appropriate errors
|
7
|
+
|
1
8
|
# v0.12.0
|
2
9
|
- hc --input option
|
3
10
|
- rails 4 support for unmunged_request_params
|
@@ -1,100 +1,56 @@
|
|
1
|
-
# the contents of this file are to let you halt a controller in its processing without having to have a
|
2
|
-
# return in the actual action. this lets helper methods which do things like parameter validation halt.
|
3
|
-
#
|
4
|
-
# it is desgined to function similarly to Sinatra's handling of throw(:halt), but is based around exceptions
|
5
|
-
# because rails doesn't catch anything, just rescues.
|
6
|
-
|
7
1
|
module ApiHammer
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
render_options = halt.render_options ? halt.render_options.dup : {}
|
32
|
-
# rocket pants does not have a render method, just render_json
|
33
|
-
if respond_to?(:render_json, true)
|
34
|
-
render_json(halt.body || {}, render_options)
|
35
|
-
else
|
36
|
-
render_options[:json] = halt.body || {}
|
37
|
-
render(render_options)
|
38
|
-
end
|
39
|
-
end
|
40
|
-
|
41
|
-
# halt and render the given body
|
42
|
-
def halt(status, body, render_options = {})
|
43
|
-
raise(ApiHammer::Halt.new(body.inspect, body, render_options.merge(:status => status)))
|
44
|
-
end
|
45
|
-
|
46
|
-
# halt and render the given errors in the body on the 'errors' key
|
47
|
-
def halt_error(status, errors, options = {})
|
48
|
-
errors_as_json = errors.respond_to?(:as_json) ? errors.as_json : errors
|
49
|
-
unless errors_as_json.is_a?(Hash)
|
50
|
-
raise ArgumentError, "errors be an object representable in JSON as a Hash; got errors = #{errors.inspect}"
|
51
|
-
end
|
52
|
-
unless errors_as_json.keys.all? { |k| k.is_a?(String) || k.is_a?(Symbol) }
|
53
|
-
raise ArgumentError, "errors keys must all be string or symbol; got errors = #{errors.inspect}"
|
54
|
-
end
|
55
|
-
unless errors_as_json.values.all? { |v| v.is_a?(Array) && v.all? { |e| e.is_a?(String) } }
|
56
|
-
raise ArgumentError, "errors values must all be arrays of strings; got errors = #{errors.inspect}"
|
57
|
-
end
|
58
|
-
render_options = options.dup.with_indifferent_access
|
59
|
-
body = {'errors' => errors}
|
60
|
-
error_message = render_options.delete('error_message') || begin
|
61
|
-
error_values = errors.values.inject([], &:+)
|
62
|
-
if error_values.size <= 1
|
63
|
-
error_values.first
|
64
|
-
else
|
65
|
-
# sentencify with periods
|
66
|
-
error_values.map { |v| v =~ /\.\s*\z/ ? v : v + '.' }.join(' ')
|
2
|
+
module HaltMethods
|
3
|
+
# halt and render the given errors in the body on the 'errors' key
|
4
|
+
def halt_error(status, errors, options = {})
|
5
|
+
errors_as_json = errors.respond_to?(:as_json) ? errors.as_json : errors
|
6
|
+
unless errors_as_json.is_a?(Hash)
|
7
|
+
raise ArgumentError, "errors be an object representable in JSON as a Hash; got errors = #{errors.inspect}"
|
8
|
+
end
|
9
|
+
unless errors_as_json.keys.all? { |k| k.is_a?(String) || k.is_a?(Symbol) }
|
10
|
+
raise ArgumentError, "errors keys must all be string or symbol; got errors = #{errors.inspect}"
|
11
|
+
end
|
12
|
+
unless errors_as_json.values.all? { |v| v.is_a?(Array) && v.all? { |e| e.is_a?(String) } }
|
13
|
+
raise ArgumentError, "errors values must all be arrays of strings; got errors = #{errors.inspect}"
|
14
|
+
end
|
15
|
+
halt_options = options.dup.with_indifferent_access
|
16
|
+
body = {'errors' => errors}
|
17
|
+
error_message = halt_options.delete('error_message') || begin
|
18
|
+
error_values = errors.values.inject([], &:+)
|
19
|
+
if error_values.size <= 1
|
20
|
+
error_values.first
|
21
|
+
else
|
22
|
+
# sentencify with periods
|
23
|
+
error_values.map { |v| v =~ /\.\s*\z/ ? v : v + '.' }.join(' ')
|
24
|
+
end
|
67
25
|
end
|
26
|
+
body['error_message'] = error_message if error_message
|
27
|
+
halt(status, body, halt_options)
|
28
|
+
end
|
29
|
+
|
30
|
+
# attempts to find and return the given model (an ActiveRecord::Base subclass) with the given attributes.
|
31
|
+
# halts with 404 (does not return) if that fails. options[:status] may specify a different status if that
|
32
|
+
# is required.
|
33
|
+
#
|
34
|
+
# e.g.:
|
35
|
+
#
|
36
|
+
# find_or_halt(User, :email => 'user@example.com')
|
37
|
+
#
|
38
|
+
def find_or_halt(model, find_attrs, options={})
|
39
|
+
options = {:status => 404}.merge(options)
|
40
|
+
record = model.where(find_attrs).first
|
41
|
+
unless record
|
42
|
+
attributes = find_attrs.map { |attr, val| "#{attr}: #{val}" }.join(", ")
|
43
|
+
model_name = model.table_name
|
44
|
+
model_name = model_name.singularize if model_name.respond_to?(:singularize)
|
45
|
+
message = I18n.t(:"errors.unknown_record_with_attributes", :default => "Unknown %{model_name}! %{attributes}",
|
46
|
+
:model_name => model_name,
|
47
|
+
:attributes => attributes
|
48
|
+
)
|
49
|
+
halt_error(options[:status], {model_name => [message]})
|
50
|
+
end
|
51
|
+
record
|
68
52
|
end
|
69
|
-
body['error_message'] = error_message if error_message
|
70
|
-
halt(status, body, render_options)
|
71
|
-
end
|
72
|
-
|
73
|
-
# attempts to find and return the given model (an ActiveRecord::Base subclass) with the given attributes.
|
74
|
-
# halts with 404 (does not return) if that fails. options[:status] may specify a different status if that
|
75
|
-
# is required.
|
76
|
-
#
|
77
|
-
# e.g.:
|
78
|
-
#
|
79
|
-
# find_or_halt(User, :email => 'user@example.com')
|
80
|
-
#
|
81
|
-
def find_or_halt(model, find_attrs, options={})
|
82
|
-
options = {:status => 404}.merge(options)
|
83
|
-
record = model.where(find_attrs).first
|
84
|
-
unless record
|
85
|
-
attributes = find_attrs.map{|attr, val| "#{attr}: #{val}" }.join(", ")
|
86
|
-
model_name = model.table_name
|
87
|
-
model_name = model_name.singularize if model_name.respond_to?(:singularize)
|
88
|
-
message = I18n.t(:"errors.unknown_record_with_attributes", :default => "Unknown %{model_name}! %{attributes}",
|
89
|
-
:model_name => model_name,
|
90
|
-
:attributes => attributes
|
91
|
-
)
|
92
|
-
halt_error(options[:status], {model_name => [message]})
|
93
|
-
end
|
94
|
-
record
|
95
|
-
end
|
96
53
|
|
97
|
-
module HaltMethods
|
98
54
|
# halt with status 200 OK, responding with the given body object
|
99
55
|
def halt_ok(body, render_options = {})
|
100
56
|
halt(200, body, render_options)
|
@@ -444,6 +400,4 @@ end.compact.join)
|
|
444
400
|
|
445
401
|
=end
|
446
402
|
end
|
447
|
-
|
448
|
-
include HaltMethods
|
449
403
|
end
|
@@ -0,0 +1,47 @@
|
|
1
|
+
require 'api_hammer/halt_methods'
|
2
|
+
|
3
|
+
# the contents of this file are to let you halt a controller in its processing without having to have a
|
4
|
+
# return in the actual action. this lets helper methods which do things like parameter validation halt.
|
5
|
+
#
|
6
|
+
# it is desgined to function similarly to Sinatra's handling of throw(:halt), but is based around exceptions
|
7
|
+
# because rails doesn't catch anything, just rescues.
|
8
|
+
|
9
|
+
module ApiHammer::Rails
|
10
|
+
# an exception raised to stop processing an action and render the body given as the 'body' argument
|
11
|
+
# (which is expected to be a JSON-able object)
|
12
|
+
class Halt < StandardError
|
13
|
+
def initialize(message, body, render_options={})
|
14
|
+
super(message)
|
15
|
+
@body = body
|
16
|
+
@render_options = render_options
|
17
|
+
end
|
18
|
+
|
19
|
+
attr_reader :body, :render_options
|
20
|
+
end
|
21
|
+
|
22
|
+
unless instance_variables.any? { |iv| iv.to_s == '@halt_included' }
|
23
|
+
@halt_included = proc do |controller_class|
|
24
|
+
controller_class.send(:rescue_from, ApiHammer::Rails::Halt, :with => :handle_halt)
|
25
|
+
end
|
26
|
+
(@on_included ||= Set.new) << @halt_included
|
27
|
+
end
|
28
|
+
|
29
|
+
# handle a raised ApiHammer::Halt or subclass and render it
|
30
|
+
def handle_halt(halt)
|
31
|
+
render_options = halt.render_options ? halt.render_options.dup : {}
|
32
|
+
# rocket pants does not have a render method, just render_json
|
33
|
+
if respond_to?(:render_json, true)
|
34
|
+
render_json(halt.body || {}, render_options)
|
35
|
+
else
|
36
|
+
render_options[:json] = halt.body || {}
|
37
|
+
render(render_options)
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
# halt and render the given body
|
42
|
+
def halt(status, body, render_options = {})
|
43
|
+
raise(ApiHammer::Rails::Halt.new(body.inspect, body, render_options.merge(:status => status)))
|
44
|
+
end
|
45
|
+
|
46
|
+
include ApiHammer::HaltMethods
|
47
|
+
end
|
data/lib/api_hammer/rails.rb
CHANGED
@@ -1,11 +1,13 @@
|
|
1
|
-
require 'api_hammer/halt'
|
2
|
-
require 'api_hammer/check_required_params'
|
3
|
-
require 'api_hammer/unmunged_request_params'
|
1
|
+
require 'api_hammer/rails/halt'
|
2
|
+
require 'api_hammer/rails/check_required_params'
|
3
|
+
require 'api_hammer/rails/unmunged_request_params'
|
4
4
|
|
5
|
-
module ApiHammer
|
6
|
-
|
7
|
-
(
|
8
|
-
|
5
|
+
module ApiHammer
|
6
|
+
module Rails
|
7
|
+
def self.included(klass)
|
8
|
+
(@on_included || []).each do |included_proc|
|
9
|
+
included_proc.call(klass)
|
10
|
+
end
|
9
11
|
end
|
10
12
|
end
|
11
13
|
end
|
@@ -0,0 +1,14 @@
|
|
1
|
+
require 'api_hammer/halt_methods'
|
2
|
+
|
3
|
+
module ApiHammer
|
4
|
+
module Sinatra
|
5
|
+
module Halt
|
6
|
+
# halt and render the given body
|
7
|
+
def halt(status, body, render_options = {})
|
8
|
+
throw :halt, format_response(status, body, headers)
|
9
|
+
end
|
10
|
+
|
11
|
+
include HaltMethods
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
@@ -0,0 +1,157 @@
|
|
1
|
+
require 'api_hammer/sinatra/halt'
|
2
|
+
|
3
|
+
module ApiHammer
|
4
|
+
module Sinatra
|
5
|
+
def self.included(klass)
|
6
|
+
(@on_included || []).each do |included_proc|
|
7
|
+
included_proc.call(klass)
|
8
|
+
end
|
9
|
+
end
|
10
|
+
|
11
|
+
# override Sinatra::Base#route_missing
|
12
|
+
def route_missing
|
13
|
+
message = I18n.t('app.errors.request.route_404',
|
14
|
+
:default => "Not a known route: %{method} %{path}",
|
15
|
+
:method => env['REQUEST_METHOD'], :path => env['PATH_INFO']
|
16
|
+
)
|
17
|
+
halt_error(404, {'route' => [message]})
|
18
|
+
end
|
19
|
+
|
20
|
+
include ApiHammer::Sinatra::Halt
|
21
|
+
|
22
|
+
unless instance_variables.any? { |iv| iv.to_s == '@supported_media_types_included' }
|
23
|
+
@supported_media_types_included = proc do |klass|
|
24
|
+
klass.define_singleton_method(:supported_media_types=) do |supported_media_types|
|
25
|
+
@supported_media_types = supported_media_types
|
26
|
+
end
|
27
|
+
klass.define_singleton_method(:supported_media_types) do
|
28
|
+
@supported_media_types
|
29
|
+
end
|
30
|
+
end
|
31
|
+
(@on_included ||= Set.new) << @supported_media_types_included
|
32
|
+
end
|
33
|
+
|
34
|
+
def supported_media_types
|
35
|
+
self.class.supported_media_types
|
36
|
+
end
|
37
|
+
|
38
|
+
# finds the best match (highest q) for those supported_media_types indicated as acceptable by the Accept header.
|
39
|
+
#
|
40
|
+
# If the Accept header is not present, assumes that any supported media type is acceptable, and returns the first
|
41
|
+
# one.
|
42
|
+
#
|
43
|
+
# if the :halt_if_unacceptable option is true and no supported media type is acceptable, this halts with 406.
|
44
|
+
#
|
45
|
+
# if the :halt_if_unacceptable option is false (or omitted) and no supported media type is acceptable, this
|
46
|
+
# returns the first supported media type.
|
47
|
+
def response_media_type(options={})
|
48
|
+
options = {:halt_if_unacceptable => false}.merge(options)
|
49
|
+
accept = env['HTTP_ACCEPT']
|
50
|
+
if accept =~ /\S/
|
51
|
+
begin
|
52
|
+
best_media_type = env['rack-accept.request'].best_media_type(supported_media_types)
|
53
|
+
rescue RuntimeError => e
|
54
|
+
# TODO: this is a crappy way to recognize this exception
|
55
|
+
raise unless e.message =~ /Invalid header value/
|
56
|
+
end
|
57
|
+
if best_media_type
|
58
|
+
best_media_type
|
59
|
+
else
|
60
|
+
if options[:halt_if_unacceptable]
|
61
|
+
logger.error "received Accept header of #{accept.inspect}; halting with 406"
|
62
|
+
message = I18n.t('app.errors.request.accept',
|
63
|
+
:default => "The request indicated that no supported media type is acceptable. Supported media types are: %{supported_media_types}. The request specified Accept: %{accept}",
|
64
|
+
:accept => accept,
|
65
|
+
:supported_media_types => supported_media_types.join(', ')
|
66
|
+
)
|
67
|
+
halt_error(406, {'Accept' => [message]})
|
68
|
+
else
|
69
|
+
supported_media_types.first
|
70
|
+
end
|
71
|
+
end
|
72
|
+
else
|
73
|
+
supported_media_types.first
|
74
|
+
end
|
75
|
+
end
|
76
|
+
|
77
|
+
def check_accept
|
78
|
+
response_media_type(:halt_if_unacceptable => true)
|
79
|
+
end
|
80
|
+
|
81
|
+
# returns a rack response with the given object encoded in the appropriate format for the requests.
|
82
|
+
#
|
83
|
+
# arguments are in the order of what tends to vary most frequently
|
84
|
+
# rather than rack's way, so headers come last
|
85
|
+
def format_response(status, body_object, headers={})
|
86
|
+
body = case response_media_type
|
87
|
+
when 'application/json'
|
88
|
+
JSON.pretty_generate(body_object)
|
89
|
+
else
|
90
|
+
# :nocov:
|
91
|
+
raise NotImplementedError, "unsupported response media type #{response_media_type}"
|
92
|
+
# :nocov:
|
93
|
+
end
|
94
|
+
[status, headers.merge({'Content-Type' => response_media_type}), [body]]
|
95
|
+
end
|
96
|
+
|
97
|
+
# reads the request body
|
98
|
+
def request_body
|
99
|
+
# rewind in case anything in the past has left this un-rewound
|
100
|
+
request.body.rewind
|
101
|
+
request.body.read.tap do
|
102
|
+
# rewind in case anything in the future expects this to have been left rewound
|
103
|
+
request.body.rewind
|
104
|
+
end
|
105
|
+
end
|
106
|
+
|
107
|
+
# returns the parsed contents of the request body.
|
108
|
+
#
|
109
|
+
# checks the Content-Type of the request, and unless it's supported (or omitted - in which case assumed to be the
|
110
|
+
# first supported media type), halts with 415.
|
111
|
+
#
|
112
|
+
# if the body is not parseable, then halts with 400.
|
113
|
+
def parsed_body
|
114
|
+
request_media_type = request.media_type
|
115
|
+
unless request_media_type =~ /\S/
|
116
|
+
fallback = true
|
117
|
+
request_media_type = supported_media_types.first
|
118
|
+
end
|
119
|
+
case request_media_type
|
120
|
+
when 'application/json'
|
121
|
+
begin
|
122
|
+
return JSON.parse(request_body)
|
123
|
+
rescue JSON::ParserError
|
124
|
+
if fallback
|
125
|
+
t_key = 'app.errors.request.body_parse_fallback_json'
|
126
|
+
default = "Error encountered attempting to parse the request body. No Content-Type was specified and parsing as JSON failed. Supported media types are %{supported_media_types}. JSON parser error: %{error_class}: %{error_message}"
|
127
|
+
else
|
128
|
+
t_key = 'app.errors.request.body_parse_indicated_json'
|
129
|
+
default = "Error encountered attempting to parse the JSON request body: %{error_class}: %{error_message}"
|
130
|
+
end
|
131
|
+
message = I18n.t(t_key,
|
132
|
+
:default => default,
|
133
|
+
:error_class => $!.class,
|
134
|
+
:error_message => $!.message,
|
135
|
+
:supported_media_types => supported_media_types.join(', ')
|
136
|
+
)
|
137
|
+
errors = {'json' => [message]}
|
138
|
+
halt_error(400, errors)
|
139
|
+
end
|
140
|
+
else
|
141
|
+
if supported_media_types.include?(request_media_type)
|
142
|
+
# :nocov:
|
143
|
+
raise NotImplementedError, "handling request body with media type #{request_media_type} not implemented"
|
144
|
+
# :nocov:
|
145
|
+
end
|
146
|
+
logger.error "received Content-Type of #{request.content_type.inspect}; halting with 415"
|
147
|
+
message = I18n.t('app.errors.request.content_type',
|
148
|
+
:default => "Unsupported Content-Type of %{content_type} given for the request body. Supported media types are %{supported_media_types}",
|
149
|
+
:content_type => request.content_type,
|
150
|
+
:supported_media_types => supported_media_types.join(', ')
|
151
|
+
)
|
152
|
+
errors = {'Content-Type' => [message]}
|
153
|
+
halt_error(415, errors)
|
154
|
+
end
|
155
|
+
end
|
156
|
+
end
|
157
|
+
end
|
data/lib/api_hammer/version.rb
CHANGED
data/lib/api_hammer.rb
CHANGED
@@ -4,6 +4,7 @@ require 'api_hammer/version'
|
|
4
4
|
|
5
5
|
module ApiHammer
|
6
6
|
autoload :Rails, 'api_hammer/rails'
|
7
|
+
autoload :Sinatra, 'api_hammer/sinatra'
|
7
8
|
autoload :RequestLogger, 'api_hammer/request_logger'
|
8
9
|
autoload :ShowTextExceptions, 'api_hammer/show_text_exceptions'
|
9
10
|
autoload :TrailingNewline, 'api_hammer/trailing_newline'
|
@@ -24,43 +24,43 @@ describe 'ApiHammer::Rails#check_required_params' do
|
|
24
24
|
|
25
25
|
it 'is missing id' do
|
26
26
|
c = controller_with_params(:person => {:name => 'hammer', :height => '3'}, :lucky_numbers => ['2'])
|
27
|
-
err = assert_raises(ApiHammer::Halt) { c.check_required_params(checks) }
|
27
|
+
err = assert_raises(ApiHammer::Rails::Halt) { c.check_required_params(checks) }
|
28
28
|
assert_equal({'error_message' => 'id is required but was not provided', 'errors' => {'id' => ['id is required but was not provided']}}, err.body)
|
29
29
|
end
|
30
30
|
|
31
31
|
it 'is missing person' do
|
32
32
|
c = controller_with_params(:id => '99', :lucky_numbers => ['2'])
|
33
|
-
err = assert_raises(ApiHammer::Halt) { c.check_required_params(checks) }
|
33
|
+
err = assert_raises(ApiHammer::Rails::Halt) { c.check_required_params(checks) }
|
34
34
|
assert_equal({'error_message' => 'person is required but was not provided', 'errors' => {'person' => ['person is required but was not provided']}}, err.body)
|
35
35
|
end
|
36
36
|
|
37
37
|
it 'is has the wrong type for person' do
|
38
38
|
c = controller_with_params(:id => '99', :person => ['hammer', '3'], :lucky_numbers => ['2'])
|
39
|
-
err = assert_raises(ApiHammer::Halt) { c.check_required_params(checks) }
|
39
|
+
err = assert_raises(ApiHammer::Rails::Halt) { c.check_required_params(checks) }
|
40
40
|
assert_equal({'error_message' => 'person must be a Hash', 'errors' => {'person' => ['person must be a Hash']}}, err.body)
|
41
41
|
end
|
42
42
|
|
43
43
|
it 'is missing person#name' do
|
44
44
|
c = controller_with_params(:id => '99', :person => {:height => '3'}, :lucky_numbers => ['2'])
|
45
|
-
err = assert_raises(ApiHammer::Halt) { c.check_required_params(checks) }
|
45
|
+
err = assert_raises(ApiHammer::Rails::Halt) { c.check_required_params(checks) }
|
46
46
|
assert_equal({'error_message' => 'person#name is required but was not provided', 'errors' => {'person#name' => ['person#name is required but was not provided']}}, err.body)
|
47
47
|
end
|
48
48
|
|
49
49
|
it 'is missing lucky_numbers' do
|
50
50
|
c = controller_with_params(:id => '99', :person => {:name => 'hammer', :height => '3'})
|
51
|
-
err = assert_raises(ApiHammer::Halt) { c.check_required_params(checks) }
|
51
|
+
err = assert_raises(ApiHammer::Rails::Halt) { c.check_required_params(checks) }
|
52
52
|
assert_equal({'error_message' => 'lucky_numbers is required but was not provided', 'errors' => {'lucky_numbers' => ['lucky_numbers is required but was not provided']}}, err.body)
|
53
53
|
end
|
54
54
|
|
55
55
|
it 'has the wrong type for lucky_numbers' do
|
56
56
|
c = controller_with_params(:id => '99', :person => {:name => 'hammer', :height => '3'}, :lucky_numbers => '2')
|
57
|
-
err = assert_raises(ApiHammer::Halt) { c.check_required_params(checks) }
|
57
|
+
err = assert_raises(ApiHammer::Rails::Halt) { c.check_required_params(checks) }
|
58
58
|
assert_equal({'error_message' => 'lucky_numbers must be a Array', 'errors' => {'lucky_numbers' => ['lucky_numbers must be a Array']}}, err.body)
|
59
59
|
end
|
60
60
|
|
61
61
|
it 'has multiple problems' do
|
62
62
|
c = controller_with_params({})
|
63
|
-
err = assert_raises(ApiHammer::Halt) { c.check_required_params(checks) }
|
63
|
+
err = assert_raises(ApiHammer::Rails::Halt) { c.check_required_params(checks) }
|
64
64
|
assert_equal({'error_message' => 'id is required but was not provided. person is required but was not provided. lucky_numbers is required but was not provided.', 'errors' => {'id' => ['id is required but was not provided'], 'person' => ['person is required but was not provided'], 'lucky_numbers' => ['lucky_numbers is required but was not provided']}}, err.body)
|
65
65
|
end
|
66
66
|
end
|
data/test/halt_test.rb
CHANGED
@@ -14,19 +14,19 @@ class FakeController
|
|
14
14
|
end
|
15
15
|
|
16
16
|
describe 'ApiHammer::Rails#halt' do
|
17
|
-
it 'raises ApiHammer::Halt' do
|
18
|
-
haltex = assert_raises(ApiHammer::Halt) { FakeController.new.halt(200, {}) }
|
17
|
+
it 'raises ApiHammer::Rails::Halt' do
|
18
|
+
haltex = assert_raises(ApiHammer::Rails::Halt) { FakeController.new.halt(200, {}) }
|
19
19
|
assert_equal({}, haltex.body)
|
20
20
|
assert_equal(200, haltex.render_options[:status])
|
21
21
|
end
|
22
22
|
describe 'status-specific halts' do
|
23
23
|
it 'halts ok' do
|
24
|
-
haltex = assert_raises(ApiHammer::Halt) { FakeController.new.halt_ok({}) }
|
24
|
+
haltex = assert_raises(ApiHammer::Rails::Halt) { FakeController.new.halt_ok({}) }
|
25
25
|
assert_equal({}, haltex.body)
|
26
26
|
assert_equal(200, haltex.render_options[:status])
|
27
27
|
end
|
28
28
|
it 'halts unprocessable entity' do
|
29
|
-
haltex = assert_raises(ApiHammer::Halt) { FakeController.new.halt_unprocessable_entity({}) }
|
29
|
+
haltex = assert_raises(ApiHammer::Rails::Halt) { FakeController.new.halt_unprocessable_entity({}) }
|
30
30
|
assert_equal({'errors' => {}}, haltex.body)
|
31
31
|
assert_equal(422, haltex.render_options[:status])
|
32
32
|
end
|
@@ -49,7 +49,7 @@ describe 'ApiHammer::Rails#halt' do
|
|
49
49
|
define_method(:table_name) { 'record' }
|
50
50
|
end
|
51
51
|
end
|
52
|
-
haltex = assert_raises(ApiHammer::Halt) { FakeController.new.find_or_halt(model, {:id => 'anid'}) }
|
52
|
+
haltex = assert_raises(ApiHammer::Rails::Halt) { FakeController.new.find_or_halt(model, {:id => 'anid'}) }
|
53
53
|
assert_equal({'error_message' => 'Unknown record! id: anid', 'errors' => {'record' => ['Unknown record! id: anid']}}, haltex.body)
|
54
54
|
assert_equal(404, haltex.render_options[:status])
|
55
55
|
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: api_hammer
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.13.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Ethan
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2016-01-
|
11
|
+
date: 2016-01-27 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: rack
|
@@ -245,25 +245,28 @@ files:
|
|
245
245
|
- lib/api_hammer.rb
|
246
246
|
- lib/api_hammer/active_record_cache_find_by.rb
|
247
247
|
- lib/api_hammer/body.rb
|
248
|
-
- lib/api_hammer/check_required_params.rb
|
249
248
|
- lib/api_hammer/content_type_attrs.rb
|
250
249
|
- lib/api_hammer/faraday/outputter.rb
|
251
250
|
- lib/api_hammer/faraday/request_logger.rb
|
252
251
|
- lib/api_hammer/filtration/form_encoded.rb
|
253
252
|
- lib/api_hammer/filtration/json.rb
|
254
|
-
- lib/api_hammer/
|
253
|
+
- lib/api_hammer/halt_methods.rb
|
255
254
|
- lib/api_hammer/json_script_escape_helper.rb
|
256
255
|
- lib/api_hammer/public_instance_exec.rb
|
257
256
|
- lib/api_hammer/rails.rb
|
257
|
+
- lib/api_hammer/rails/check_required_params.rb
|
258
|
+
- lib/api_hammer/rails/halt.rb
|
259
|
+
- lib/api_hammer/rails/unmunged_request_params.rb
|
258
260
|
- lib/api_hammer/rails_or_sidekiq_logger.rb
|
259
261
|
- lib/api_hammer/rails_request_logging.rb
|
260
262
|
- lib/api_hammer/request_logger.rb
|
261
263
|
- lib/api_hammer/show_text_exceptions.rb
|
264
|
+
- lib/api_hammer/sinatra.rb
|
265
|
+
- lib/api_hammer/sinatra/halt.rb
|
262
266
|
- lib/api_hammer/tasks.rb
|
263
267
|
- lib/api_hammer/tasks/cucumber_pretty.rb
|
264
268
|
- lib/api_hammer/tasks/gem_available_updates.rb
|
265
269
|
- lib/api_hammer/trailing_newline.rb
|
266
|
-
- lib/api_hammer/unmunged_request_params.rb
|
267
270
|
- lib/api_hammer/version.rb
|
268
271
|
- lib/api_hammer/weblink.rb
|
269
272
|
- lib/logstash/filters/active_support_tags.rb
|
File without changes
|
File without changes
|