rack-parser 0.0.4 → 0.1.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.
data/README.md CHANGED
@@ -32,14 +32,12 @@ In a Sinatra or [Padrino](http://padrinorb.com) application, it would probably b
32
32
  ```ruby
33
33
  # app.rb
34
34
 
35
- use Rack::Parser, :content_types => {
36
- 'application/json' => Proc.new { |body| MultiJson.decode body },
37
- 'application/xml' => Proc.new { |body| MultiXml.decode body },
38
- 'application/roll' => Proc.new { |body| 'never gonna give you up' }
39
- }
35
+ use Rack::Parser
40
36
  ```
41
37
 
42
38
 
39
+ ### Content Type Parsing ###
40
+
43
41
  By default, Rack::Parser uses MultiJson and MultiXml to decode/parse
44
42
  your JSON/XML Data. these can be overwritten if you choose not to use
45
43
  them. However, through using them you can just as easily leverage the
@@ -69,6 +67,29 @@ use Rack::Parser, :content_types => {
69
67
  }
70
68
  ```
71
69
 
70
+ ### Error Handling ###
71
+
72
+ Rack::Parser comes with a default error handling response that is sent
73
+ if an error is to occur. If a `logger` is present, it will try to `warn`
74
+ with the content type and error message.
75
+
76
+ You can additionally customize the error handling response as well to
77
+ whatever it is you like:
78
+
79
+ ```ruby
80
+ use Rack::Parser, :error_responses => {
81
+ 'default' => Proc.new { |e, content_type| [500, {}, ["boo hoo"] ] },
82
+ 'application/json' => Proc.new { |e, content_type| [400, {'Content-Type'=>content_type}, ["broke"]] }
83
+ }
84
+ ```
85
+
86
+ The error handler expects to pass both the `error` and `content_type` so
87
+ that you can use them within your responses. In addition, you can
88
+ override the default response as well.
89
+
90
+ If no content_type error handling response is present, it will use the
91
+ `default`.
92
+
72
93
  ## Inspirations ##
73
94
 
74
95
  This project came to being because of:
@@ -76,6 +97,11 @@ This project came to being because of:
76
97
  * [Niko Dittmann's](https://www.github.com/niko) [rack-post-body-to-params](https://www.github.com/niko/rack-post-body-to-params) which some of its ideas are instilled in this middleware.
77
98
  * Rack::PostBodyContentTypeParser from rack-contrib which proved to be an inspiration for both libraries.
78
99
 
100
+
101
+ ## Contributors ##
102
+
103
+ * [Stephen Becker IV](https://github.com/sbeckeriv) - For initial custom error response handling work.
104
+
79
105
  ## Copyright
80
106
 
81
107
  Copyright © 2011 Arthur Chiu. See [MIT-LICENSE](https://github.com/achiu/rack-parser/blob/master/MIT-LICENSE) for details.
@@ -8,6 +8,8 @@ module Rack
8
8
  # designate any engine you wish that is compatible with the MultiJson/MultiXml libraries.
9
9
  # You can also conveniently use another library by as well by wrapping it as a Proc or add additional
10
10
  # content types which are not default in this middleware.
11
+ # In addition, you can set custom error handling for each content_type. If no error response is defined for
12
+ # a particular content_type, it will use the default error response, which can also be overrided.
11
13
  #
12
14
  class Parser
13
15
 
@@ -22,17 +24,37 @@ module Rack
22
24
  'application/json' => Proc.new { |body| MultiJson.decode(body) }
23
25
  }
24
26
 
25
- attr_reader :content_types
27
+ DEFAULT_ERROR_RESPONSE = {
28
+ 'default' =>
29
+ Proc.new do |e, content_type|
30
+ format = content_type.split('/').last
31
+ meth = "to_#{format}"
32
+ meth = "inspect" unless ::Hash.respond_to? meth
33
+ [400, {'Content-Type' => content_type }, [ { 'errors' => e.to_s }.method(meth).call ] ]
34
+ end
35
+ }
36
+
37
+ attr_reader :content_types, :error_responses
26
38
 
27
39
  # Usage:
28
- # use Rack::Parser, :content_types = {
40
+ # use Rack::Parser, :content_types => {
29
41
  # 'application/xml' => Proc.new { |body| XmlParser.parse body } # if you don't want the default
30
42
  # 'application/json' => Proc.new { |body| JsonParser.decode body } # if you don't want the default
31
- # 'application/foo' => Proc.new { |body| FooParser.parse body }
43
+ # 'application/foo' => Proc.new { |body| FooParser.parse body } # Add custom content_types to parse.
32
44
  # }
45
+ #
46
+ # # use Rack::Parser,
47
+ # :content_types => {
48
+ # 'application/xml' => Proc.new { |body| XmlParser.parse body } # if you don't want the default
49
+ # },
50
+ # :error_responses => {
51
+ # 'default' => Proc.new { |e, content_type| [500, {}, ["boo hoo"] ] }, # Override the default error response..
52
+ # 'application/json' => Proc.new { |e, content_type| [400, {'Content-Type'=>content_type}, ["broke"]] } # Customize error responses based on content type.
53
+ # }
33
54
  def initialize(app, options = {})
34
- @app = app
35
- @content_types = DEFAULT_CONTENT_TYPE.merge(options.delete(:content_types) || {})
55
+ @app = app
56
+ @content_types = DEFAULT_CONTENT_TYPE.merge(options.delete(:content_types) || {})
57
+ @error_responses = DEFAULT_ERROR_RESPONSE.merge(options.delete(:error_responses) || {})
36
58
  end
37
59
 
38
60
  def call(env)
@@ -43,16 +65,14 @@ module Rack
43
65
  body = env[POST_BODY].read
44
66
  return @app.call(env) if (body.respond_to?(:empty?) ? body.empty? : !body) # Send it down the stack immediately
45
67
  content_type = Rack::Request.new(env).media_type
46
- format = content_type.split('/').last
47
68
  begin
48
69
  result = @content_types[content_type].call(body)
49
70
  env.update FORM_HASH => result, FORM_INPUT => env[POST_BODY]
50
71
  @app.call env
51
72
  rescue Exception => e
52
- logger.warn "#{self.class} #{content_type} parsing error: #{e.to_s}" if respond_to? :logger # Send to logger if its there.
53
- meth = "to_#{format}"
54
- meth = "inspect" unless Hash.respond_to? meth
55
- [400, { 'Content-Type' => content_type }, [ {'errors' => e.to_s}.method(meth).call ] ] # Finally, return an error response.
73
+ logger.warn "#{self.class} #{content_type} parsing error: #{e.to_s}" if respond_to? :logger # Send to logger if its there.
74
+ err = @error_responses[content_type] ? content_type : 'default'
75
+ @error_responses[err].call(e, content_type) # call the error responses
56
76
  end
57
77
  end
58
78
 
@@ -3,7 +3,7 @@ $:.push File.expand_path("../lib", __FILE__)
3
3
 
4
4
  Gem::Specification.new do |s|
5
5
  s.name = "rack-parser"
6
- s.version = "0.0.4"
6
+ s.version = "0.1.0"
7
7
  s.authors = ["Arthur Chiu"]
8
8
  s.email = ["mr.arthur.chiu@gmail.com"]
9
9
  s.homepage = "https://www.github.com/achiu/rack-parser"
@@ -54,13 +54,25 @@ context "Rack::Parser" do
54
54
  asserts(:body).equals({'foo' => 'bar'}.inspect)
55
55
  end
56
56
 
57
- context "with bad data" do
58
- setup do
59
- post '/post', "fuuuuuuuuuu", { 'CONTENT_TYPE' => 'application/json' }
57
+ context "for errors" do
58
+
59
+ context "with default error message" do
60
+ setup do
61
+ post '/post', "fuuuuuuuuuu", { 'CONTENT_TYPE' => 'application/json' }
62
+ end
63
+
64
+ asserts(:status).equals 400
65
+ asserts(:body).equals "{\"errors\":\"710: unexpected token at 'fuuuuuuuuuu'\"}"
60
66
  end
61
67
 
62
- asserts(:status).equals 400
63
- asserts(:body).equals "{\"errors\":\"706: unexpected token at 'fuuuuuuuuuu'\"}"
68
+ context "with custom default error message" do
69
+ setup do
70
+ post '/post', "fuuuuuuuuuu", { 'CONTENT_TYPE' => 'application/wahh' }
71
+ end
72
+
73
+ asserts(:status).equals 500
74
+ asserts(:body).equals "wahh"
75
+ end
64
76
  end
65
77
 
66
78
  end
@@ -24,7 +24,13 @@ class Riot::Situation
24
24
  }
25
25
 
26
26
  builder = Rack::Builder.new
27
- builder.use Rack::Parser, :content_types => { 'application/foo' => Proc.new { |b| {'foo' => 'bar'} } }
27
+ builder.use Rack::Parser,
28
+ :content_types => {
29
+ 'application/foo' => Proc.new { |b| {'foo' => 'bar'} }
30
+ },
31
+ :error_responses => {
32
+ 'application/wahh' => Proc.new { |e, content_type| [500,{'Content-Type' => content_type},['wahh']]}
33
+ }
28
34
  builder.run main_app
29
35
  builder.to_app
30
36
  end
metadata CHANGED
@@ -1,93 +1,90 @@
1
- --- !ruby/object:Gem::Specification
1
+ --- !ruby/object:Gem::Specification
2
2
  name: rack-parser
3
- version: !ruby/object:Gem::Version
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
4
5
  prerelease:
5
- version: 0.0.4
6
6
  platform: ruby
7
- authors:
7
+ authors:
8
8
  - Arthur Chiu
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
-
13
- date: 2011-07-15 00:00:00 Z
14
- dependencies:
15
- - !ruby/object:Gem::Dependency
12
+ date: 2011-08-19 00:00:00.000000000Z
13
+ dependencies:
14
+ - !ruby/object:Gem::Dependency
16
15
  name: rack
17
- prerelease: false
18
- requirement: &id001 !ruby/object:Gem::Requirement
16
+ requirement: &2152833180 !ruby/object:Gem::Requirement
19
17
  none: false
20
- requirements:
21
- - - ">="
22
- - !ruby/object:Gem::Version
23
- version: "0"
18
+ requirements:
19
+ - - ! '>='
20
+ - !ruby/object:Gem::Version
21
+ version: '0'
24
22
  type: :runtime
25
- version_requirements: *id001
26
- - !ruby/object:Gem::Dependency
27
- name: multi_json
28
23
  prerelease: false
29
- requirement: &id002 !ruby/object:Gem::Requirement
24
+ version_requirements: *2152833180
25
+ - !ruby/object:Gem::Dependency
26
+ name: multi_json
27
+ requirement: &2152832740 !ruby/object:Gem::Requirement
30
28
  none: false
31
- requirements:
32
- - - ">="
33
- - !ruby/object:Gem::Version
34
- version: "0"
29
+ requirements:
30
+ - - ! '>='
31
+ - !ruby/object:Gem::Version
32
+ version: '0'
35
33
  type: :runtime
36
- version_requirements: *id002
37
- - !ruby/object:Gem::Dependency
38
- name: multi_xml
39
34
  prerelease: false
40
- requirement: &id003 !ruby/object:Gem::Requirement
35
+ version_requirements: *2152832740
36
+ - !ruby/object:Gem::Dependency
37
+ name: multi_xml
38
+ requirement: &2152832320 !ruby/object:Gem::Requirement
41
39
  none: false
42
- requirements:
43
- - - ">="
44
- - !ruby/object:Gem::Version
45
- version: "0"
40
+ requirements:
41
+ - - ! '>='
42
+ - !ruby/object:Gem::Version
43
+ version: '0'
46
44
  type: :runtime
47
- version_requirements: *id003
48
- - !ruby/object:Gem::Dependency
49
- name: riot
50
45
  prerelease: false
51
- requirement: &id004 !ruby/object:Gem::Requirement
46
+ version_requirements: *2152832320
47
+ - !ruby/object:Gem::Dependency
48
+ name: riot
49
+ requirement: &2152831900 !ruby/object:Gem::Requirement
52
50
  none: false
53
- requirements:
54
- - - ">="
55
- - !ruby/object:Gem::Version
56
- version: "0"
51
+ requirements:
52
+ - - ! '>='
53
+ - !ruby/object:Gem::Version
54
+ version: '0'
57
55
  type: :development
58
- version_requirements: *id004
59
- - !ruby/object:Gem::Dependency
60
- name: rack-test
61
56
  prerelease: false
62
- requirement: &id005 !ruby/object:Gem::Requirement
57
+ version_requirements: *2152831900
58
+ - !ruby/object:Gem::Dependency
59
+ name: rack-test
60
+ requirement: &2152831480 !ruby/object:Gem::Requirement
63
61
  none: false
64
- requirements:
65
- - - ">="
66
- - !ruby/object:Gem::Version
67
- version: "0"
62
+ requirements:
63
+ - - ! '>='
64
+ - !ruby/object:Gem::Version
65
+ version: '0'
68
66
  type: :development
69
- version_requirements: *id005
70
- - !ruby/object:Gem::Dependency
71
- name: json
72
67
  prerelease: false
73
- requirement: &id006 !ruby/object:Gem::Requirement
68
+ version_requirements: *2152831480
69
+ - !ruby/object:Gem::Dependency
70
+ name: json
71
+ requirement: &2152831060 !ruby/object:Gem::Requirement
74
72
  none: false
75
- requirements:
76
- - - ">="
77
- - !ruby/object:Gem::Version
78
- version: "0"
73
+ requirements:
74
+ - - ! '>='
75
+ - !ruby/object:Gem::Version
76
+ version: '0'
79
77
  type: :development
80
- version_requirements: *id006
81
- description: Rack Middleware for parsing post body data for json/xml and various content types
82
- email:
78
+ prerelease: false
79
+ version_requirements: *2152831060
80
+ description: Rack Middleware for parsing post body data for json/xml and various content
81
+ types
82
+ email:
83
83
  - mr.arthur.chiu@gmail.com
84
84
  executables: []
85
-
86
85
  extensions: []
87
-
88
86
  extra_rdoc_files: []
89
-
90
- files:
87
+ files:
91
88
  - .gitignore
92
89
  - Gemfile
93
90
  - MIT-LICENSE
@@ -99,31 +96,28 @@ files:
99
96
  - test/teststrap.rb
100
97
  homepage: https://www.github.com/achiu/rack-parser
101
98
  licenses: []
102
-
103
99
  post_install_message:
104
100
  rdoc_options: []
105
-
106
- require_paths:
101
+ require_paths:
107
102
  - lib
108
- required_ruby_version: !ruby/object:Gem::Requirement
103
+ required_ruby_version: !ruby/object:Gem::Requirement
109
104
  none: false
110
- requirements:
111
- - - ">="
112
- - !ruby/object:Gem::Version
113
- version: "0"
114
- required_rubygems_version: !ruby/object:Gem::Requirement
105
+ requirements:
106
+ - - ! '>='
107
+ - !ruby/object:Gem::Version
108
+ version: '0'
109
+ required_rubygems_version: !ruby/object:Gem::Requirement
115
110
  none: false
116
- requirements:
117
- - - ">="
118
- - !ruby/object:Gem::Version
119
- version: "0"
111
+ requirements:
112
+ - - ! '>='
113
+ - !ruby/object:Gem::Version
114
+ version: '0'
120
115
  requirements: []
121
-
122
116
  rubyforge_project: rack-parser
123
- rubygems_version: 1.8.4
117
+ rubygems_version: 1.8.6
124
118
  signing_key:
125
119
  specification_version: 3
126
120
  summary: Rack Middleware for parsing post body data
127
- test_files:
121
+ test_files:
128
122
  - test/parser_test.rb
129
123
  - test/teststrap.rb