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 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