rack-parser 0.0.1

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.
@@ -0,0 +1,4 @@
1
+ *.gem
2
+ .bundle
3
+ Gemfile.lock
4
+ pkg/*
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source "http://rubygems.org"
2
+
3
+ # Specify your gem's dependencies in rack-parser.gemspec
4
+ gemspec
@@ -0,0 +1,21 @@
1
+ Copyright (c) 2011 Arthur Chiu
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining
4
+ a copy of this software and associated documentation files (the
5
+ "Software"), to deal in the Software without restriction, including
6
+ without limitation the rights to use, copy, modify, merge, publish,
7
+ distribute, sublicense, and/or sell copies of the Software, and to
8
+ permit persons to whom the Software is furnished to do so, subject to
9
+ the following conditions:
10
+
11
+ The above copyright notice and this permission notice shall be
12
+ included in all copies or substantial portions of the Software.
13
+
14
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
21
+
@@ -0,0 +1,82 @@
1
+ # Rack::Parser #
2
+
3
+ Rack::Parser is a Rack Middleware for parsing post body data for JSON/XML, and custom
4
+ content types using MultiJson, MultiXML, or any thing that you want to
5
+ use.
6
+
7
+ What this allows your rack application to do is decode/parse incoming post data
8
+ into param hashes for your applications to use.
9
+
10
+ ## Installation ##
11
+
12
+ install it via rubygems:
13
+
14
+ ```
15
+ gem install rack-parser
16
+ ```
17
+
18
+ or put it in your Gemfile:
19
+
20
+ ```ruby
21
+ # Gemfile
22
+
23
+ gem 'rack-parser', :require => 'rack/parser'
24
+ ```
25
+
26
+
27
+ ## Usage ##
28
+
29
+
30
+ In a Sinatra or [Padrino](http://padrinorb.com) application, it would probably be something like:
31
+
32
+ ```ruby
33
+ # app.rb
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
+ }
40
+ ```
41
+
42
+
43
+ By default, Rack::Parser uses MultiJson and MultiXml to decode/parse
44
+ your JSON/XML Data. these can be overwritten if you choose not to use
45
+ them. However, through using them you can just as easily leverage the
46
+ engine of your choice by setting the engine like so:
47
+
48
+
49
+ ```ruby
50
+ # app.rb
51
+
52
+ MultiJson.engine = :yajl # Yajl-ruby for json decoding
53
+ MultiXml.parser = :libxml # libxml for XML parsing
54
+
55
+ use Rack::Parser, :content_types => {
56
+ 'application/json' => Proc.new { |body| MultiJson.decode body },
57
+ 'application/xml' => Proc.new { |body| MultiXml.decode body },
58
+ 'application/roll' => Proc.new { |body| 'never gonna give you up' }
59
+ }
60
+ ```
61
+
62
+ To set your own custom engine that perhaps neither MultiJson or MultiXml
63
+ support, just make it a Proc:
64
+
65
+
66
+ ```ruby
67
+ use Rack::Parser, :content_types => {
68
+ 'application/json' => Proc.new { |body| MyCustomJsonEngine.do_it body }
69
+ }
70
+ ```
71
+
72
+ ## Inspirations ##
73
+
74
+ This project came to being because of:
75
+
76
+ * [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
+ * Rack::PostBodyContentTypeParser from rack-contrib which proved to be an inspiration for both libraries.
78
+
79
+ ## Copyright
80
+
81
+ Copyright © 2011 Arthur Chiu. See [MIT-LICENSE](https://github.com/achiu/rack-parser/blob/master/MIT-LICENSE) for details.
82
+
@@ -0,0 +1,9 @@
1
+ require 'bundler/gem_tasks'
2
+
3
+ require 'rake/testtask'
4
+ Rake::TestTask.new(:test) do |test|
5
+ test.libs << 'lib' << 'test'
6
+ test.pattern = 'test/**/*_test.rb'
7
+ test.verbose = true
8
+ test.ruby_opts << '-rubygems'
9
+ end
@@ -0,0 +1,59 @@
1
+ require 'multi_json'
2
+ require 'multi_xml'
3
+
4
+ module Rack
5
+
6
+ # Rack::Parser allows you to set custom parsers for incoming post body data. As a default,
7
+ # Rack::Parser uses MultiJson and MultiXml to do the decoding/parsing for you. This allows you to
8
+ # designate any engine you wish that is compatible with the MultiJson/MultiXml libraries.
9
+ # You can also conveniently use another library by as well by wrapping it as a Proc or add additional
10
+ # content types which are not default in this middleware.
11
+ #
12
+ class Parser
13
+
14
+ # Rack Constants
15
+ HEADER_CONTENT_TYPE = 'Content-Type'.freeze
16
+ POST_BODY = 'rack.input'.freeze
17
+ FORM_INPUT = 'rack.request.form_input'.freeze
18
+ FORM_HASH = 'rack.request.form_hash'.freeze
19
+
20
+ # Default Settings
21
+ DEFAULT_CONTENT_TYPE = {
22
+ 'application/xml' => Proc.new { |body| MultiXml.parse(body) },
23
+ 'application/json' => Proc.new { |body| MultiJson.decode(body) }
24
+ }
25
+
26
+ attr_reader :content_types
27
+
28
+ # Usage:
29
+ # use Rack::Parser, :content_types = {
30
+ # 'application/xml' => Proc.new { |body| XmlParser.parse body } # if you don't want the default
31
+ # 'application/json' => Proc.new { |body| JsonParser.decode body } # if you don't want the default
32
+ # 'application/foo' => Proc.new { |body| FooParser.parse body }
33
+ # }
34
+ def initialize(app, options = {})
35
+ @app = app
36
+ @content_types = DEFAULT_CONTENT_TYPE.merge(options.delete(:content_types) || {})
37
+ end
38
+
39
+ def call(env)
40
+ dup._call(env)
41
+ end
42
+
43
+ def _call(env)
44
+ body = env[POST_BODY].read
45
+ return @app.call(env) if (body.respond_to?(:empty?) ? body.empty? : !body) # Send it down the stack immediately
46
+ content_type = env[HEADER_CONTENT_TYPE]
47
+ format = content_type.split('/').last
48
+ begin
49
+ result = @content_types[content_type].call(body)
50
+ env.update FORM_HASH => result, FORM_INPUT => body
51
+ @app.call env
52
+ rescue Exception => e
53
+ logger.warn "#{self.class} #{content_type} parsing error: #{e.to_s}" if respond_to? :logger # Send to logger if its there.
54
+ [400, { 'Content-Type' => content_type }, [ {'errors' => e.to_s}.method("to_#{format}").call ] ] # Finally, return an error response.
55
+ end
56
+ end
57
+
58
+ end
59
+ end
@@ -0,0 +1,26 @@
1
+ # -*- encoding: utf-8 -*-
2
+ $:.push File.expand_path("../lib", __FILE__)
3
+
4
+ Gem::Specification.new do |s|
5
+ s.name = "rack-parser"
6
+ s.version = "0.0.1"
7
+ s.authors = ["Arthur Chiu"]
8
+ s.email = ["mr.arthur.chiu@gmail.com"]
9
+ s.homepage = ""
10
+ s.summary = %q{Rack Middleware for parsing post body data}
11
+ s.description = %q{Rack Middleware for parsing post body data for json/xml and various content types}
12
+
13
+ s.rubyforge_project = "rack-parser"
14
+
15
+ s.files = `git ls-files`.split("\n")
16
+ s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
17
+ s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
18
+ s.require_paths = ["lib"]
19
+
20
+ s.add_dependency 'rack'
21
+ s.add_dependency 'multi_json'
22
+ s.add_dependency 'multi_xml'
23
+ s.add_development_dependency 'riot'
24
+ s.add_development_dependency 'rack-test'
25
+ s.add_development_dependency 'json'
26
+ end
@@ -0,0 +1,66 @@
1
+ require File.expand_path('../teststrap', __FILE__)
2
+
3
+ class FooApp
4
+ def call(env); env; end
5
+ end
6
+
7
+ context "Rack::Parser" do
8
+
9
+ context "default configuration" do
10
+ setup do
11
+ Rack::Parser.new(FooApp.new).content_types
12
+ end
13
+
14
+ asserts(:[],'application/xml').kind_of Proc
15
+ asserts(:[],'application/json').kind_of Proc
16
+ end
17
+
18
+ context "with custom configuration" do
19
+ setup do
20
+ Rack::Parser.new(FooApp.new, :content_types => {
21
+ 'application/xml' => :meh,
22
+ 'application/foo' => :bar
23
+ }).content_types
24
+ end
25
+
26
+ asserts(:[], 'application/xml').equals :meh
27
+ asserts(:[], 'application/foo').equals :bar
28
+ end
29
+
30
+ context "with json" do
31
+ setup do
32
+ post '/post', "{\"test\":1,\"foo\":2,\"bar\":\"3\"}", { 'Content-Type' => 'application/json' }
33
+ end
34
+
35
+ asserts(:status).equals 200
36
+ asserts(:body).equals "{\"test\"=>1, \"foo\"=>2, \"bar\"=>\"3\"}"
37
+ end
38
+
39
+ context "with xml" do
40
+ setup do
41
+ put '/post', "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<hash>\n <a type=\"integer\">1</a>\n</hash>\n", { 'Content-Type' => 'application/xml'}
42
+ end
43
+
44
+ asserts(:status).equals 200
45
+ asserts(:body).equals "{\"hash\"=>{\"a\"=>1}}"
46
+ end
47
+
48
+ context "with custom 'foo'" do
49
+ setup do
50
+ post '/post', 'something that does not matter', { 'Content-Type' => 'application/foo' }
51
+ end
52
+
53
+ asserts(:status).equals 200
54
+ asserts(:body).equals 'foo'
55
+ end
56
+
57
+ context "with bad data" do
58
+ setup do
59
+ post '/post', "fuuuuuuuuuu", { 'Content-Type' => 'application/json' }
60
+ end
61
+
62
+ asserts(:status).equals 400
63
+ asserts(:body).equals "{\"errors\":\"706: unexpected token at 'fuuuuuuuuuu'\"}"
64
+ end
65
+
66
+ end
@@ -0,0 +1,33 @@
1
+ require 'rack'
2
+ require 'riot'
3
+ require 'rack/test'
4
+ require 'rack/builder'
5
+ require 'json'
6
+
7
+ require File.expand_path('../../lib/rack/parser', __FILE__)
8
+
9
+ class Riot::Situation
10
+ include Rack::Test::Methods
11
+
12
+ def app
13
+ main_app = lambda { |env|
14
+ request = Rack::Request.new(env)
15
+ return_code, body_text =
16
+ case request.path
17
+ when '/' then [200,'Hello world']
18
+ when '/post' then [200, env['rack.request.form_hash']]
19
+ else
20
+ [404,'Nothing here']
21
+ end
22
+ [return_code,{'Content-type' => 'text/plain'}, [body_text]]
23
+ }
24
+
25
+ builder = Rack::Builder.new
26
+ builder.use Rack::Parser, :content_types => { 'application/foo' => Proc.new { |b| 'foo' } }
27
+ builder.run main_app
28
+ builder.to_app
29
+ end
30
+ end
31
+
32
+ class Riot::Context
33
+ end
metadata ADDED
@@ -0,0 +1,129 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: rack-parser
3
+ version: !ruby/object:Gem::Version
4
+ prerelease:
5
+ version: 0.0.1
6
+ platform: ruby
7
+ authors:
8
+ - Arthur Chiu
9
+ autorequire:
10
+ bindir: bin
11
+ cert_chain: []
12
+
13
+ date: 2011-07-13 00:00:00 Z
14
+ dependencies:
15
+ - !ruby/object:Gem::Dependency
16
+ name: rack
17
+ prerelease: false
18
+ requirement: &id001 !ruby/object:Gem::Requirement
19
+ none: false
20
+ requirements:
21
+ - - ">="
22
+ - !ruby/object:Gem::Version
23
+ version: "0"
24
+ type: :runtime
25
+ version_requirements: *id001
26
+ - !ruby/object:Gem::Dependency
27
+ name: multi_json
28
+ prerelease: false
29
+ requirement: &id002 !ruby/object:Gem::Requirement
30
+ none: false
31
+ requirements:
32
+ - - ">="
33
+ - !ruby/object:Gem::Version
34
+ version: "0"
35
+ type: :runtime
36
+ version_requirements: *id002
37
+ - !ruby/object:Gem::Dependency
38
+ name: multi_xml
39
+ prerelease: false
40
+ requirement: &id003 !ruby/object:Gem::Requirement
41
+ none: false
42
+ requirements:
43
+ - - ">="
44
+ - !ruby/object:Gem::Version
45
+ version: "0"
46
+ type: :runtime
47
+ version_requirements: *id003
48
+ - !ruby/object:Gem::Dependency
49
+ name: riot
50
+ prerelease: false
51
+ requirement: &id004 !ruby/object:Gem::Requirement
52
+ none: false
53
+ requirements:
54
+ - - ">="
55
+ - !ruby/object:Gem::Version
56
+ version: "0"
57
+ type: :development
58
+ version_requirements: *id004
59
+ - !ruby/object:Gem::Dependency
60
+ name: rack-test
61
+ prerelease: false
62
+ requirement: &id005 !ruby/object:Gem::Requirement
63
+ none: false
64
+ requirements:
65
+ - - ">="
66
+ - !ruby/object:Gem::Version
67
+ version: "0"
68
+ type: :development
69
+ version_requirements: *id005
70
+ - !ruby/object:Gem::Dependency
71
+ name: json
72
+ prerelease: false
73
+ requirement: &id006 !ruby/object:Gem::Requirement
74
+ none: false
75
+ requirements:
76
+ - - ">="
77
+ - !ruby/object:Gem::Version
78
+ version: "0"
79
+ type: :development
80
+ version_requirements: *id006
81
+ description: Rack Middleware for parsing post body data for json/xml and various content types
82
+ email:
83
+ - mr.arthur.chiu@gmail.com
84
+ executables: []
85
+
86
+ extensions: []
87
+
88
+ extra_rdoc_files: []
89
+
90
+ files:
91
+ - .gitignore
92
+ - Gemfile
93
+ - MIT-LICENSE
94
+ - README.md
95
+ - Rakefile
96
+ - lib/rack/parser.rb
97
+ - rack-parser.gemspec
98
+ - test/parser_test.rb
99
+ - test/teststrap.rb
100
+ homepage: ""
101
+ licenses: []
102
+
103
+ post_install_message:
104
+ rdoc_options: []
105
+
106
+ require_paths:
107
+ - lib
108
+ required_ruby_version: !ruby/object:Gem::Requirement
109
+ none: false
110
+ requirements:
111
+ - - ">="
112
+ - !ruby/object:Gem::Version
113
+ version: "0"
114
+ required_rubygems_version: !ruby/object:Gem::Requirement
115
+ none: false
116
+ requirements:
117
+ - - ">="
118
+ - !ruby/object:Gem::Version
119
+ version: "0"
120
+ requirements: []
121
+
122
+ rubyforge_project: rack-parser
123
+ rubygems_version: 1.8.4
124
+ signing_key:
125
+ specification_version: 3
126
+ summary: Rack Middleware for parsing post body data
127
+ test_files:
128
+ - test/parser_test.rb
129
+ - test/teststrap.rb