rack-post-body-to-params 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/.document ADDED
@@ -0,0 +1,5 @@
1
+ README.rdoc
2
+ lib/**/*.rb
3
+ bin/*
4
+ features/**/*.feature
5
+ LICENSE
data/.gitignore ADDED
@@ -0,0 +1,21 @@
1
+ ## MAC OS
2
+ .DS_Store
3
+
4
+ ## TEXTMATE
5
+ *.tmproj
6
+ tmtags
7
+
8
+ ## EMACS
9
+ *~
10
+ \#*
11
+ .\#*
12
+
13
+ ## VIM
14
+ *.swp
15
+
16
+ ## PROJECT::GENERAL
17
+ coverage
18
+ rdoc
19
+ pkg
20
+
21
+ ## PROJECT::SPECIFIC
data/LICENSE ADDED
@@ -0,0 +1,20 @@
1
+ Copyright (c) 2009 Niko Dittmann
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.
data/README.rdoc ADDED
@@ -0,0 +1,56 @@
1
+ = Rack::PostBodyToParams
2
+
3
+ Parses the POST or PUT body to a Hash and put it into the FORM_HASH. Most frameworks get the params hash from there.
4
+
5
+ You want to build a XMl or JSON API not with Rails? Perhaps Sinatra[http://www.sinatrarb.com/] or Padrino[http://www.padrinorb.com/]? Are you missing the Rails feature that the body of XML and JSON POST or PUT requests are automatically parsed into the params hash? Here you go!
6
+
7
+ There is something similar already in the {rack-contrib gem}[http://github.com/rack/rack-contrib] called Rack::PostBodyContentTypeParser. Besides being even less creative with the naming Rack::PostBodyToParams sports these features:
8
+
9
+ * Parses XML as well.
10
+ * Uses ActiveSupport. This may also considered to be a bug but it's in almost every web-framework anyway. The following features sort of depend on it.
11
+ * Uses ActiveSupports settings for XML & JSON parsing, mainly the parser that's used. Including Nokogiri[http://nokogiri.org/] and YAJL[http://github.com/brianmario/yajl-ruby] for more speed and less memory consumption.
12
+ * Parsers are configurable.
13
+ * Parse errors are returned with as status 400 and the error message of the parser.
14
+
15
+ == Howto use
16
+
17
+ use PostBodyToParams
18
+
19
+ or
20
+
21
+ use Rack::PostBodyContentTypeParser,
22
+ :content_types => ['application/xml'],
23
+ :parsers => {
24
+ 'application/xml' => Proc.new{|a| my_own_xml_parser a },
25
+ 'application/foo' => Proc.new{|a| my_foo_parser a }
26
+ }
27
+
28
+ As you can see you can restrict Rack::PostBodyToParams to only respond to 'application/xml' or 'application/json'.
29
+
30
+ Make shure ActiveSupport is required and configured. Should look somewhat like this:
31
+
32
+ ActiveSupport::JSON.backend = 'Yajl'
33
+ ActiveSupport::XmlMini.backend = 'Nokogiri'
34
+
35
+ Note that all current versions of ActiveSupport (up to 3.0.0.beta4) have a bug when requiring the yajl gem. See here for details:
36
+ {Rails Ticket 4897}[https://rails.lighthouseapp.com/projects/8994/tickets/4897-yajl-backend-discovery-fails-in-activesupportjson].
37
+
38
+ Either you have to patch your ActiveSupport gem or use another parser:
39
+
40
+ ActiveSupport::JSON.backend = 'YAML'
41
+
42
+ == This is early release software. Bug reports and (even more so) fixes are highly welcome!
43
+
44
+ == Note on Patches/Pull Requests
45
+
46
+ * Fork the project.
47
+ * Make your feature addition or bug fix.
48
+ * Add tests for it. This is important so I don't break it in a
49
+ future version unintentionally.
50
+ * Commit, do not mess with rakefile, version, or history.
51
+ (if you want to have your own version, that is fine but bump version in a commit by itself I can ignore when I pull)
52
+ * Send me a pull request. Bonus points for topic branches.
53
+
54
+ == Copyright
55
+
56
+ Copyright (c) 2010 Niko Dittmann. See LICENSE for details.
data/Rakefile ADDED
@@ -0,0 +1,54 @@
1
+ require 'rubygems'
2
+ require 'rake'
3
+
4
+ begin
5
+ require 'jeweler'
6
+ Jeweler::Tasks.new do |gem|
7
+ gem.name = "rack-post-body-to-params"
8
+ gem.summary = %Q{Parses the POST or PUT body to a Hash and put it into the FORM_HASH. Most frameworks get the params hash from there.}
9
+
10
+ gem.email = "mail+git@niko-dittmann.com"
11
+ gem.homepage = "http://github.com/niko/rack-post-body-to-params"
12
+ gem.authors = ["Niko Dittmann"]
13
+
14
+ gem.add_development_dependency "shoulda", ">= 0"
15
+ gem.add_runtime_dependency "activesupport", ">= 2.3"
16
+ end
17
+ Jeweler::GemcutterTasks.new
18
+ rescue LoadError
19
+ puts "Jeweler (or a dependency) not available. Install it with: gem install jeweler"
20
+ end
21
+
22
+ require 'rake/testtask'
23
+ Rake::TestTask.new(:test) do |test|
24
+ test.libs << 'lib' << 'test'
25
+ test.pattern = 'test/**/test_*.rb'
26
+ test.verbose = true
27
+ end
28
+
29
+ begin
30
+ require 'rcov/rcovtask'
31
+ Rcov::RcovTask.new do |test|
32
+ test.libs << 'test'
33
+ test.pattern = 'test/**/test_*.rb'
34
+ test.verbose = true
35
+ end
36
+ rescue LoadError
37
+ task :rcov do
38
+ abort "RCov is not available. In order to run rcov, you must: sudo gem install spicycode-rcov"
39
+ end
40
+ end
41
+
42
+ task :test => :check_dependencies
43
+
44
+ task :default => :test
45
+
46
+ require 'rake/rdoctask'
47
+ Rake::RDocTask.new do |rdoc|
48
+ version = File.exist?('VERSION') ? File.read('VERSION') : ""
49
+
50
+ rdoc.rdoc_dir = 'rdoc'
51
+ rdoc.title = "rack-post-body-to-params #{version}"
52
+ rdoc.rdoc_files.include('README*')
53
+ rdoc.rdoc_files.include('lib/**/*.rb')
54
+ end
data/VERSION ADDED
@@ -0,0 +1 @@
1
+ 0.1.0
@@ -0,0 +1,100 @@
1
+ # gem 'activesupport', '=2.3.8'
2
+ gem 'activesupport', '=3.0.0.beta4'
3
+ require 'active_support'
4
+ require 'active_support/core_ext/hash'
5
+
6
+ module Rack
7
+
8
+ # A Rack middleware for parsing POST/PUT body data when
9
+ # Content-Type is <tt>application/json</tt> or <tt>application/xml</tt>.
10
+ #
11
+ # Uses ActiveSupport::JSON.decode for json and ActiveSupports enhanced Hash
12
+ # #from_xml for xml. Be shure to have ActiveSupport required beforehand.
13
+ #
14
+ # Configure parsers for ActiveSupport (you should do this perhaps anyway):
15
+ #
16
+ # ActiveSupport::JSON.backend = 'Yajl'
17
+ # ActiveSupport::XmlMini.backend = 'Nokogiri'
18
+ #
19
+ # concerning Yajl: https://rails.lighthouseapp.com/projects/8994/tickets/4897-yajl-backend-discovery-fails-in-activesupportjson
20
+ #
21
+ # Note that all parsing errors will be rescued and returned back to the client.
22
+ #
23
+ # Most parts blantly stolen from http://github.com/rack/rack-contrib.
24
+ #
25
+ class PostBodyToParams
26
+
27
+ # Constants
28
+ #
29
+ CONTENT_TYPE = 'CONTENT_TYPE'.freeze
30
+ POST_BODY = 'rack.input'.freeze
31
+ FORM_INPUT = 'rack.request.form_input'.freeze
32
+ FORM_HASH = 'rack.request.form_hash'.freeze
33
+
34
+ # Supported Content-Types
35
+ #
36
+ APPLICATION_JSON = 'application/json'.freeze
37
+ APPLICATION_XML = 'application/xml'.freeze
38
+
39
+ attr_reader :parsers, :error_responses
40
+
41
+ # Override the parsers and the error responses as needed:
42
+ #
43
+ # use Rack::PostBodyContentTypeParser,
44
+ # :content_types => ['application/xml'],
45
+ # :parsers => {
46
+ # 'application/xml' => Proc.new{|a| my_own_xml_parser a },
47
+ # 'application/foo' => Proc.new{|a| my_foo_parser a }
48
+ # }
49
+ #
50
+ def initialize(app, config={})
51
+ @content_types = config.delete(:content_types) || [APPLICATION_JSON, APPLICATION_XML]
52
+
53
+ @parsers = {
54
+ APPLICATION_JSON => Proc.new{ |post_body| parse_as_json post_body },
55
+ APPLICATION_XML => Proc.new{ |post_body| parse_as_xml post_body }
56
+ }
57
+ @parsers.update(config[:parsers]) if config[:parsers]
58
+
59
+ @error_responses = {
60
+ APPLICATION_JSON => Proc.new{ |error| json_error_response error },
61
+ APPLICATION_XML => Proc.new{ |error| xml_error_response error }
62
+ }
63
+ @error_responses.update(config[:error_responses]) if config[:error_responses]
64
+
65
+ @app = app
66
+ end
67
+
68
+ def parse_as_xml(xml_data)
69
+ Hash.from_xml xml_data
70
+ end
71
+ def parse_as_json(json_data)
72
+ ActiveSupport::JSON.decode json_data
73
+ end
74
+
75
+ def json_error_response(error)
76
+ [ 400, {'Content-Type' => APPLICATION_JSON}, [ {"json-syntax-error" => error.to_s}.to_json ] ]
77
+ end
78
+ def xml_error_response(error)
79
+ [ 400, {'Content-Type' => APPLICATION_XML}, [ {"xml-syntax-error" => error.to_s}.to_xml(:root => :errors) ] ]
80
+ end
81
+
82
+ def call(env)
83
+ content_type = env[CONTENT_TYPE]
84
+
85
+ if @content_types.include? content_type
86
+ post_body = env[POST_BODY].read
87
+ begin
88
+ new_form_hash = parsers[content_type].call post_body
89
+ rescue Exception => error
90
+ logger.warn "#{self.class} #{content_type} parsing error: #{error.to_s}" if respond_to? :logger
91
+ return error_responses[content_type].call error
92
+ end
93
+ env.update(FORM_HASH => new_form_hash, FORM_INPUT => env[POST_BODY])
94
+ end
95
+
96
+ @app.call(env)
97
+ end
98
+
99
+ end
100
+ end
@@ -0,0 +1,56 @@
1
+ # Generated by jeweler
2
+ # DO NOT EDIT THIS FILE DIRECTLY
3
+ # Instead, edit Jeweler::Tasks in Rakefile, and run the gemspec command
4
+ # -*- encoding: utf-8 -*-
5
+
6
+ Gem::Specification.new do |s|
7
+ s.name = %q{rack-post-body-to-params}
8
+ s.version = "0.1.0"
9
+
10
+ s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
11
+ s.authors = ["Niko Dittmann"]
12
+ s.date = %q{2010-06-21}
13
+ s.email = %q{mail+git@niko-dittmann.com}
14
+ s.extra_rdoc_files = [
15
+ "LICENSE",
16
+ "README.rdoc"
17
+ ]
18
+ s.files = [
19
+ ".document",
20
+ ".gitignore",
21
+ "LICENSE",
22
+ "README.rdoc",
23
+ "Rakefile",
24
+ "VERSION",
25
+ "lib/rack/post-body-to-params.rb",
26
+ "rack-post-body-to-params.gemspec",
27
+ "test/helper.rb",
28
+ "test/test_post-body-to-params.rb"
29
+ ]
30
+ s.homepage = %q{http://github.com/niko/rack-post-body-to-params}
31
+ s.rdoc_options = ["--charset=UTF-8"]
32
+ s.require_paths = ["lib"]
33
+ s.rubygems_version = %q{1.3.7}
34
+ s.summary = %q{Parses the POST or PUT body to a Hash and put it into the FORM_HASH. Most frameworks get the params hash from there.}
35
+ s.test_files = [
36
+ "test/helper.rb",
37
+ "test/test_post-body-to-params.rb"
38
+ ]
39
+
40
+ if s.respond_to? :specification_version then
41
+ current_version = Gem::Specification::CURRENT_SPECIFICATION_VERSION
42
+ s.specification_version = 3
43
+
44
+ if Gem::Version.new(Gem::VERSION) >= Gem::Version.new('1.2.0') then
45
+ s.add_development_dependency(%q<shoulda>, [">= 0"])
46
+ s.add_runtime_dependency(%q<activesupport>, [">= 2.3"])
47
+ else
48
+ s.add_dependency(%q<shoulda>, [">= 0"])
49
+ s.add_dependency(%q<activesupport>, [">= 2.3"])
50
+ end
51
+ else
52
+ s.add_dependency(%q<shoulda>, [">= 0"])
53
+ s.add_dependency(%q<activesupport>, [">= 2.3"])
54
+ end
55
+ end
56
+
data/test/helper.rb ADDED
@@ -0,0 +1,10 @@
1
+ require 'rubygems'
2
+ require 'test/unit'
3
+ require 'shoulda'
4
+
5
+ $LOAD_PATH.unshift(File.join(File.dirname(__FILE__), '..', 'lib'))
6
+ $LOAD_PATH.unshift(File.dirname(__FILE__))
7
+ require 'rack/post-body-to-params'
8
+
9
+ class Test::Unit::TestCase
10
+ end
@@ -0,0 +1,123 @@
1
+ require 'helper'
2
+ # ActiveSupport::JSON.backend = 'JSONGem'
3
+ ActiveSupport::JSON.backend = 'Yajl'
4
+ ActiveSupport::XmlMini.backend = 'Nokogiri'
5
+
6
+ class TestApp
7
+ def call(env)
8
+ return env
9
+ end
10
+ end
11
+
12
+ module FromXml
13
+ def from_xml(data)
14
+ "parsed #{data}"
15
+ end
16
+ end
17
+
18
+ class Logger
19
+ def warn(string)
20
+ "warning: #{string}"
21
+ end
22
+ end
23
+
24
+ class TestPostBodyToParams < Test::Unit::TestCase
25
+
26
+ context "A new app" do
27
+ context "without further configuration" do
28
+ setup do
29
+ @test_app = TestApp.new
30
+ @app = Rack::PostBodyToParams.new @test_app
31
+ end
32
+ should "have the default content_types" do
33
+ assert_equal ['application/json','application/xml'], @app.instance_variable_get('@content_types').sort
34
+ end
35
+ should "have the default parsers" do
36
+ assert @app.parsers.keys.include? 'application/json'
37
+ assert @app.parsers.keys.include? 'application/xml'
38
+ assert @app.parsers['application/json'].is_a? Proc
39
+ assert @app.parsers['application/xml'].is_a? Proc
40
+ end
41
+ should "have the default error responses" do
42
+ assert @app.error_responses.keys.include? 'application/json'
43
+ assert @app.error_responses.keys.include? 'application/xml'
44
+ assert @app.error_responses['application/json'].is_a? Proc
45
+ assert @app.error_responses['application/xml'].is_a? Proc
46
+ end
47
+ end
48
+
49
+ context "with further configuration" do
50
+ should "have different content_types" do
51
+ app = Rack::PostBodyToParams.new @test_app, :content_types => [:fu]
52
+ assert_equal [:fu], app.instance_variable_get('@content_types')
53
+ end
54
+ should "have different parsers" do
55
+ app = Rack::PostBodyToParams.new @test_app, :parsers => {'application/json' => :bar}
56
+ assert_equal :bar, app.parsers['application/json']
57
+ end
58
+ should "have different error responses" do
59
+ app = Rack::PostBodyToParams.new @test_app, :error_responses => {'application/json' => :baz}
60
+ assert_equal :baz, app.error_responses['application/json']
61
+ end
62
+ end
63
+ end
64
+
65
+ context "the parsers" do
66
+ setup do
67
+ @test_app = TestApp.new
68
+ @app = Rack::PostBodyToParams.new @test_app
69
+ end
70
+ should "return the error as xml" do
71
+ error = 'some error occured'
72
+ response = [400, {"Content-Type"=>"application/xml"}, ["<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<errors>\n <xml-syntax-error>some error occured</xml-syntax-error>\n</errors>\n"]]
73
+ assert_equal response, @app.xml_error_response(error)
74
+ end
75
+ should "return the error as json" do
76
+ error = 'some error occured'
77
+ response = [400, {"Content-Type"=>"application/json"}, ["{\"json-syntax-error\":\"some error occured\"}"]]
78
+ assert_equal response, @app.json_error_response(error)
79
+ end
80
+ end
81
+
82
+ context "the error responses" do
83
+ setup do
84
+ end
85
+ end
86
+
87
+ context "the app itself" do
88
+ setup do
89
+ @test_app = TestApp.new
90
+ @app = Rack::PostBodyToParams.new @test_app
91
+ end
92
+ should "put json string data into the form_hash" do
93
+ env = {
94
+ 'CONTENT_TYPE' => 'application/json',
95
+ 'rack.input' => StringIO.new('{"bla":"blub"}')
96
+ }
97
+ new_env = @app.call(env)
98
+ assert_equal({"bla"=>"blub"}, new_env['rack.request.form_hash'])
99
+ assert_equal env['rack.input'], new_env['rack.request.form_input']
100
+ end
101
+ should "return 400 and the error message on faulty json" do
102
+ env = {
103
+ 'CONTENT_TYPE' => 'application/json',
104
+ 'rack.input' => StringIO.new('{"bla":"blub"}}')
105
+ }
106
+ code, header, body = @app.call(env)
107
+ assert_equal 400, code
108
+ assert_equal 'application/json', header['Content-Type']
109
+ assert_match /json-syntax-error/, body.first
110
+ end
111
+ should "return 400 and the error message on faulty xml" do
112
+ env = {
113
+ 'CONTENT_TYPE' => 'application/xml',
114
+ 'rack.input' => StringIO.new("<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<track>\n<title>\nfo\n</title>")
115
+ }
116
+ code, header, body = @app.call(env)
117
+ assert_equal 400, code
118
+ assert_equal 'application/xml', header['Content-Type']
119
+ assert_match /xml-syntax-error/, body.first
120
+ end
121
+ end
122
+
123
+ end
metadata ADDED
@@ -0,0 +1,106 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: rack-post-body-to-params
3
+ version: !ruby/object:Gem::Version
4
+ hash: 27
5
+ prerelease: false
6
+ segments:
7
+ - 0
8
+ - 1
9
+ - 0
10
+ version: 0.1.0
11
+ platform: ruby
12
+ authors:
13
+ - Niko Dittmann
14
+ autorequire:
15
+ bindir: bin
16
+ cert_chain: []
17
+
18
+ date: 2010-06-21 00:00:00 +02:00
19
+ default_executable:
20
+ dependencies:
21
+ - !ruby/object:Gem::Dependency
22
+ name: shoulda
23
+ prerelease: false
24
+ requirement: &id001 !ruby/object:Gem::Requirement
25
+ none: false
26
+ requirements:
27
+ - - ">="
28
+ - !ruby/object:Gem::Version
29
+ hash: 3
30
+ segments:
31
+ - 0
32
+ version: "0"
33
+ type: :development
34
+ version_requirements: *id001
35
+ - !ruby/object:Gem::Dependency
36
+ name: activesupport
37
+ prerelease: false
38
+ requirement: &id002 !ruby/object:Gem::Requirement
39
+ none: false
40
+ requirements:
41
+ - - ">="
42
+ - !ruby/object:Gem::Version
43
+ hash: 5
44
+ segments:
45
+ - 2
46
+ - 3
47
+ version: "2.3"
48
+ type: :runtime
49
+ version_requirements: *id002
50
+ description:
51
+ email: mail+git@niko-dittmann.com
52
+ executables: []
53
+
54
+ extensions: []
55
+
56
+ extra_rdoc_files:
57
+ - LICENSE
58
+ - README.rdoc
59
+ files:
60
+ - .document
61
+ - .gitignore
62
+ - LICENSE
63
+ - README.rdoc
64
+ - Rakefile
65
+ - VERSION
66
+ - lib/rack/post-body-to-params.rb
67
+ - rack-post-body-to-params.gemspec
68
+ - test/helper.rb
69
+ - test/test_post-body-to-params.rb
70
+ has_rdoc: true
71
+ homepage: http://github.com/niko/rack-post-body-to-params
72
+ licenses: []
73
+
74
+ post_install_message:
75
+ rdoc_options:
76
+ - --charset=UTF-8
77
+ require_paths:
78
+ - lib
79
+ required_ruby_version: !ruby/object:Gem::Requirement
80
+ none: false
81
+ requirements:
82
+ - - ">="
83
+ - !ruby/object:Gem::Version
84
+ hash: 3
85
+ segments:
86
+ - 0
87
+ version: "0"
88
+ required_rubygems_version: !ruby/object:Gem::Requirement
89
+ none: false
90
+ requirements:
91
+ - - ">="
92
+ - !ruby/object:Gem::Version
93
+ hash: 3
94
+ segments:
95
+ - 0
96
+ version: "0"
97
+ requirements: []
98
+
99
+ rubyforge_project:
100
+ rubygems_version: 1.3.7
101
+ signing_key:
102
+ specification_version: 3
103
+ summary: Parses the POST or PUT body to a Hash and put it into the FORM_HASH. Most frameworks get the params hash from there.
104
+ test_files:
105
+ - test/helper.rb
106
+ - test/test_post-body-to-params.rb