api_hammer 0.12.0 → 0.13.0
Sign up to get free protection for your applications and to get access to all the features.
- 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
|