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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 32321326726e5c0d8d195dfb62c59fbaeb6837ec
4
- data.tar.gz: f770e32fc9bd8ee16d1f17d6c30b4dceeca03131
3
+ metadata.gz: a4967ef9a4193e2caca1f27035eb81f81e1ed918
4
+ data.tar.gz: 61f6a27ec7f13762954656f841666dddc37c9a8c
5
5
  SHA512:
6
- metadata.gz: 843b147c0c7d60776834ffe0ea3d2c9e8c25e42558ea398df825478217969309af8700a2cbafe3ffc1f885a61adcbb183d80a495881a664b508850aa94a00cc5
7
- data.tar.gz: 2202a0ebb1fbf519a37ecd5ab223418a3e38254616c7367d1dbb0da8b1c72fd1d860db831fa5031012949bc49a81284b224dfb22500f98d93bece4c010c84216
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
- # an exception raised to stop processing an action and render the body given as the 'body' argument
9
- # (which is expected to be a JSON-able object)
10
- class Halt < StandardError
11
- def initialize(message, body, render_options={})
12
- super(message)
13
- @body = body
14
- @render_options = render_options
15
- end
16
-
17
- attr_reader :body, :render_options
18
- end
19
- end
20
-
21
- module ApiHammer::Rails
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::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::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
@@ -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::Rails
6
- def self.included(klass)
7
- (@on_included || []).each do |included_proc|
8
- included_proc.call(klass)
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
@@ -1,3 +1,3 @@
1
1
  module ApiHammer
2
- VERSION = "0.12.0"
2
+ VERSION = "0.13.0"
3
3
  end
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.12.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-22 00:00:00.000000000 Z
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/halt.rb
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