├── .gitignore ├── .travis.yml ├── Gemfile ├── LICENSE ├── README.md ├── Rakefile ├── actionpack-xml_parser.gemspec ├── gemfiles └── Gemfile-edge ├── lib ├── action_pack │ ├── xml_parser.rb │ └── xml_parser │ │ ├── railtie.rb │ │ └── version.rb └── actionpack-xml_parser.rb └── test ├── fixtures └── 500.html ├── helper.rb ├── webservice_test.rb └── xml_params_parsing_test.rb /.gitignore: -------------------------------------------------------------------------------- 1 | *.gem 2 | *.rbc 3 | .bundle 4 | .config 5 | .yardoc 6 | *.lock 7 | InstalledFiles 8 | _yardoc 9 | coverage 10 | doc/ 11 | lib/bundler/man 12 | pkg 13 | rdoc 14 | spec/reports 15 | test/tmp 16 | test/version_tmp 17 | tmp 18 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: ruby 2 | sudo: false 3 | rvm: 4 | - 2.2.5 5 | - 2.3.1 6 | - ruby-head 7 | gemfile: 8 | - gemfiles/Gemfile-edge 9 | - Gemfile 10 | notifications: 11 | email: false 12 | matrix: 13 | fast_finish: true 14 | allow_failures: 15 | - rvm: ruby-head 16 | cache: bundler 17 | -------------------------------------------------------------------------------- /Gemfile: -------------------------------------------------------------------------------- 1 | source 'https://rubygems.org' 2 | 3 | gemspec 4 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2013 Prem Sichanugrist 2 | 3 | MIT License 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining 6 | a copy of this software and associated documentation files (the 7 | "Software"), to deal in the Software without restriction, including 8 | without limitation the rights to use, copy, modify, merge, publish, 9 | distribute, sublicense, and/or sell copies of the Software, and to 10 | permit persons to whom the Software is furnished to do so, subject to 11 | the following conditions: 12 | 13 | The above copyright notice and this permission notice shall be 14 | included in all copies or substantial portions of the Software. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 17 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 18 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 19 | NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE 20 | LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 21 | OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION 22 | WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 23 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | actionpack-xml\_parser 2 | ====================== 3 | 4 | A XML parameters parser for Action Pack (removed from core in Rails 4.0) 5 | 6 | Installation 7 | ------------ 8 | 9 | Include this gem into your Gemfile: 10 | 11 | ```ruby 12 | gem 'actionpack-xml_parser' 13 | ``` 14 | 15 | Parameters parsing rules 16 | ------------------------ 17 | 18 | The parameters parsing is handled by `ActiveSupport::XMLConverter` so there may 19 | be specific features and subtle differences depending on the chosen XML backend. 20 | 21 | ### Hashes 22 | 23 | Basically, each node represents a key. With the following XML: 24 | 25 | ```xml 26 | David 27 | ``` 28 | 29 | The resulting parameters will be: 30 | 31 | ```ruby 32 | {"person" => {"name" => "David"}} 33 | ``` 34 | 35 | ### File attachment 36 | 37 | You can specify the `type` attribute of a node to attach files: 38 | 39 | ```xml 40 | 41 | 42 | 43 | ``` 44 | 45 | The resulting parameters will include a `StringIO` object with the given content, 46 | name and content type set accordingly: 47 | 48 | ```ruby 49 | {"person" => {"avatar" => #}} 50 | ``` 51 | 52 | ### Arrays 53 | 54 | There are several ways to pass an array. You can either specify multiple nodes 55 | with the same name: 56 | 57 | ```xml 58 | 59 |
60 |
61 | 62 | ``` 63 | 64 | The resulting parameters will be: 65 | 66 | ```ruby 67 | {"person" => {"address" => [{"city" => "Chicago"}, {"city" => "Ottawa"}]}} 68 | ``` 69 | 70 | You can also specify the `type` attribute of a node and nest child nodes inside: 71 | 72 | ```xml 73 | 74 | 75 |
76 |
77 | 78 | 79 | ``` 80 | 81 | will result in: 82 | 83 | ```ruby 84 | {"person" => {"addresses" => [{"city" => "Melbourne"}, {"city" => "Paris"}]}} 85 | ``` 86 | -------------------------------------------------------------------------------- /Rakefile: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env rake 2 | require "bundler/gem_tasks" 3 | require 'rake/testtask' 4 | 5 | Rake::TestTask.new do |t| 6 | t.libs = ["test"] 7 | t.pattern = "test/**/*_test.rb" 8 | t.ruby_opts = ['-w'] 9 | end 10 | 11 | task :default => :test 12 | -------------------------------------------------------------------------------- /actionpack-xml_parser.gemspec: -------------------------------------------------------------------------------- 1 | $:.push File.expand_path("../lib", __FILE__) 2 | require "action_pack/xml_parser/version" 3 | 4 | Gem::Specification.new do |s| 5 | s.platform = Gem::Platform::RUBY 6 | s.name = 'actionpack-xml_parser' 7 | s.version = ActionPack::XmlParser::VERSION 8 | s.summary = 'XML parameters parser for Action Pack (removed from core in Rails 4.0)' 9 | 10 | s.required_ruby_version = '>= 2.2.2' 11 | s.license = 'MIT' 12 | 13 | s.author = 'Prem Sichanugrist' 14 | s.email = 's@sikac.hu' 15 | s.homepage = 'http://www.rubyonrails.org' 16 | 17 | s.files = Dir['LICENSE', 'README.md', 'lib/**/*'] 18 | s.require_path = 'lib' 19 | 20 | s.extra_rdoc_files = %w( README.md ) 21 | s.rdoc_options.concat ['--main', 'README.md'] 22 | 23 | s.add_dependency('actionpack', '>= 5.0') 24 | s.add_dependency('railties', '>= 5.0') 25 | 26 | s.add_development_dependency('rake') 27 | end 28 | -------------------------------------------------------------------------------- /gemfiles/Gemfile-edge: -------------------------------------------------------------------------------- 1 | source 'https://rubygems.org' 2 | 3 | gemspec path: '..' 4 | 5 | gem 'actionpack', github: 'rails/rails', branch: 'master' 6 | gem 'railties', github: 'rails/rails', branch: 'master' 7 | -------------------------------------------------------------------------------- /lib/action_pack/xml_parser.rb: -------------------------------------------------------------------------------- 1 | require 'active_support' 2 | require 'active_support/core_ext/hash/conversions' 3 | require 'action_dispatch' 4 | require 'action_dispatch/http/request' 5 | require 'action_pack/xml_parser/version' 6 | 7 | module ActionPack 8 | class XmlParser 9 | def self.register 10 | original_parsers = ActionDispatch::Request.parameter_parsers 11 | ActionDispatch::Request.parameter_parsers = original_parsers.merge(Mime[:xml].symbol => self) 12 | end 13 | 14 | def self.call(raw_post) 15 | Hash.from_xml(raw_post) || {} 16 | end 17 | end 18 | end 19 | -------------------------------------------------------------------------------- /lib/action_pack/xml_parser/railtie.rb: -------------------------------------------------------------------------------- 1 | require 'action_pack/xml_parser' 2 | 3 | module ActionPack 4 | class XmlParser 5 | class Railtie < ::Rails::Railtie 6 | initializer "actionpack-xml_parser.configure" do 7 | ActionPack::XmlParser.register 8 | end 9 | end 10 | end 11 | end 12 | -------------------------------------------------------------------------------- /lib/action_pack/xml_parser/version.rb: -------------------------------------------------------------------------------- 1 | module ActionPack 2 | class XmlParser 3 | VERSION = "2.0.1" 4 | end 5 | end 6 | -------------------------------------------------------------------------------- /lib/actionpack-xml_parser.rb: -------------------------------------------------------------------------------- 1 | require 'action_pack/xml_parser/railtie' if defined?(Rails::Railtie) 2 | -------------------------------------------------------------------------------- /test/fixtures/500.html: -------------------------------------------------------------------------------- 1 | 500 error fixture 2 | -------------------------------------------------------------------------------- /test/helper.rb: -------------------------------------------------------------------------------- 1 | require 'bundler/setup' 2 | 3 | require 'active_support/testing/autorun' 4 | require 'action_pack/xml_parser' 5 | require 'action_controller' 6 | 7 | FIXTURE_LOAD_PATH = File.join(File.dirname(__FILE__), 'fixtures') 8 | SharedTestRoutes = ActionDispatch::Routing::RouteSet.new 9 | 10 | module ActionDispatch 11 | module SharedRoutes 12 | def before_setup 13 | @routes = SharedTestRoutes 14 | super 15 | end 16 | end 17 | end 18 | 19 | class RoutedRackApp 20 | attr_reader :routes 21 | 22 | def initialize(routes, &blk) 23 | @routes = routes 24 | @stack = ActionDispatch::MiddlewareStack.new(&blk).build(@routes) 25 | end 26 | 27 | def call(env) 28 | @stack.call(env) 29 | end 30 | end 31 | 32 | class ActionDispatch::IntegrationTest < ActiveSupport::TestCase 33 | include ActionDispatch::SharedRoutes 34 | 35 | ActionPack::XmlParser.register 36 | 37 | def self.build_app(routes = nil) 38 | RoutedRackApp.new(routes || ActionDispatch::Routing::RouteSet.new) do |middleware| 39 | middleware.use ActionDispatch::ShowExceptions, ActionDispatch::PublicExceptions.new(FIXTURE_LOAD_PATH) 40 | middleware.use Rack::Head 41 | yield(middleware) if block_given? 42 | end 43 | end 44 | 45 | self.app = build_app 46 | 47 | # Stub Rails dispatcher so it does not get controller references and 48 | # simply return the controller#action as Rack::Body. 49 | class StubDispatcher < ::ActionDispatch::Routing::RouteSet::Dispatcher 50 | protected 51 | def controller_reference(controller_param) 52 | controller_param 53 | end 54 | 55 | def dispatch(controller, action, env) 56 | [200, {'Content-Type' => 'text/html'}, ["#{controller}##{action}"]] 57 | end 58 | end 59 | 60 | def self.stub_controllers 61 | old_dispatcher = ActionDispatch::Routing::RouteSet::Dispatcher 62 | ActionDispatch::Routing::RouteSet.module_eval { remove_const :Dispatcher } 63 | ActionDispatch::Routing::RouteSet.module_eval { const_set :Dispatcher, StubDispatcher } 64 | yield ActionDispatch::Routing::RouteSet.new 65 | ensure 66 | ActionDispatch::Routing::RouteSet.module_eval { remove_const :Dispatcher } 67 | ActionDispatch::Routing::RouteSet.module_eval { const_set :Dispatcher, old_dispatcher } 68 | end 69 | 70 | def with_routing(&block) 71 | temporary_routes = ActionDispatch::Routing::RouteSet.new 72 | old_app, self.class.app = self.class.app, self.class.build_app(temporary_routes) 73 | old_routes = SharedTestRoutes 74 | silence_warnings { Object.const_set(:SharedTestRoutes, temporary_routes) } 75 | 76 | yield temporary_routes 77 | ensure 78 | self.class.app = old_app 79 | silence_warnings { Object.const_set(:SharedTestRoutes, old_routes) } 80 | end 81 | end 82 | -------------------------------------------------------------------------------- /test/webservice_test.rb: -------------------------------------------------------------------------------- 1 | require 'helper' 2 | 3 | class WebServiceTest < ActionDispatch::IntegrationTest 4 | class TestController < ActionController::Base 5 | def assign_parameters 6 | if params[:full] 7 | render plain: dump_params_keys 8 | else 9 | render plain: (params.keys - ['controller', 'action']).sort.join(", ") 10 | end 11 | end 12 | 13 | def dump_params_keys(hash = params) 14 | hash.keys.sort.inject("") do |s, k| 15 | value = hash[k] 16 | 17 | if value.is_a?(Hash) || value.is_a?(ActionController::Parameters) 18 | value = "(#{dump_params_keys(value)})" 19 | else 20 | value = "" 21 | end 22 | 23 | s << ", " unless s.empty? 24 | s << "#{k}#{value}" 25 | end 26 | end 27 | end 28 | 29 | def setup 30 | @controller = TestController.new 31 | @integration_session = nil 32 | end 33 | 34 | def test_check_parameters 35 | with_test_route_set do 36 | get "/" 37 | assert_equal '', @controller.response.body 38 | end 39 | end 40 | 41 | def test_post_xml 42 | with_test_route_set do 43 | post "/", params: 'content...', 44 | headers: {'CONTENT_TYPE' => 'application/xml'} 45 | 46 | assert_equal 'entry', @controller.response.body 47 | assert @controller.params.has_key?(:entry) 48 | assert_equal 'content...', @controller.params["entry"]['summary'] 49 | assert_equal 'true', @controller.params["entry"]['attributed'] 50 | end 51 | end 52 | 53 | def test_put_xml 54 | with_test_route_set do 55 | put "/", params: 'content...', 56 | headers: {'CONTENT_TYPE' => 'application/xml'} 57 | 58 | assert_equal 'entry', @controller.response.body 59 | assert @controller.params.has_key?(:entry) 60 | assert_equal 'content...', @controller.params["entry"]['summary'] 61 | assert_equal 'true', @controller.params["entry"]['attributed'] 62 | end 63 | end 64 | 65 | def test_put_xml_using_a_type_node 66 | with_test_route_set do 67 | put "/", params: 'content...', 68 | headers: {'CONTENT_TYPE' => 'application/xml'} 69 | 70 | assert_equal 'type', @controller.response.body 71 | assert @controller.params.has_key?(:type) 72 | assert_equal 'content...', @controller.params["type"]['summary'] 73 | assert_equal 'true', @controller.params["type"]['attributed'] 74 | end 75 | end 76 | 77 | def test_put_xml_using_a_type_node_and_attribute 78 | with_test_route_set do 79 | put "/", params: 'false', 80 | headers: {'CONTENT_TYPE' => 'application/xml'} 81 | 82 | assert_equal 'type', @controller.response.body 83 | assert @controller.params.has_key?(:type) 84 | assert_equal false, @controller.params["type"]['summary'] 85 | assert_equal 'true', @controller.params["type"]['attributed'] 86 | end 87 | end 88 | 89 | def test_post_xml_using_a_type_node 90 | with_test_route_set do 91 | post "/", params: 'arial', 92 | headers: {'CONTENT_TYPE' => 'application/xml'} 93 | 94 | assert_equal 'font', @controller.response.body 95 | assert @controller.params.has_key?(:font) 96 | assert_equal 'arial', @controller.params['font']['type'] 97 | assert_equal 'true', @controller.params["font"]['attributed'] 98 | end 99 | end 100 | 101 | def test_post_xml_using_a_root_node_named_type 102 | with_test_route_set do 103 | post "/", params: '33', 104 | headers: {'CONTENT_TYPE' => 'application/xml'} 105 | 106 | assert @controller.params.has_key?(:type) 107 | assert_equal 33, @controller.params['type'] 108 | end 109 | end 110 | 111 | def test_post_xml_using_an_attributted_node_named_type 112 | with_test_route_set do 113 | with_params_parsers Mime[:xml].symbol => Proc.new { |data| Hash.from_xml(data)['request'].with_indifferent_access } do 114 | post "/", params: 'Arial,123', 115 | headers: {'CONTENT_TYPE' => 'application/xml'} 116 | 117 | assert_equal 'type, z', @controller.response.body 118 | assert @controller.params.has_key?(:type) 119 | assert_equal 'Arial,12', @controller.params['type'], @controller.params.inspect 120 | assert_equal '3', @controller.params['z'], @controller.params.inspect 121 | end 122 | end 123 | end 124 | 125 | def test_post_xml_using_a_disallowed_type_attribute 126 | $stderr = StringIO.new 127 | with_test_route_set do 128 | post '/', params: 'value', headers: {'CONTENT_TYPE' => 'application/xml'} 129 | assert_response 400 130 | 131 | post '/', params: 'value', headers: {'CONTENT_TYPE' => 'application/xml'} 132 | assert_response 400 133 | end 134 | ensure 135 | $stderr = STDERR 136 | end 137 | 138 | def test_register_and_use_xml_simple 139 | with_test_route_set do 140 | with_params_parsers Mime[:xml].symbol => Proc.new { |data| Hash.from_xml(data)['request'].with_indifferent_access } do 141 | post "/", params: 'content...SimpleXml', 142 | headers: {'CONTENT_TYPE' => 'application/xml'} 143 | 144 | assert_equal 'summary, title', @controller.response.body 145 | assert @controller.params.has_key?(:summary) 146 | assert @controller.params.has_key?(:title) 147 | assert_equal 'content...', @controller.params["summary"] 148 | assert_equal 'SimpleXml', @controller.params["title"] 149 | end 150 | end 151 | end 152 | 153 | def test_use_xml_ximple_with_empty_request 154 | with_test_route_set do 155 | assert_nothing_raised { post "/", params: "", headers: {'CONTENT_TYPE' => 'application/xml'} } 156 | assert_equal '', @controller.response.body 157 | end 158 | end 159 | 160 | def test_dasherized_keys_as_xml 161 | with_test_route_set do 162 | post "/?full=1", params: "\n...\n", 163 | headers: {'CONTENT_TYPE' => 'application/xml'} 164 | assert_equal 'action, controller, first_key(sub_key), full', @controller.response.body 165 | assert_equal "...", @controller.params[:first_key][:sub_key] 166 | end 167 | end 168 | 169 | def test_typecast_as_xml 170 | with_test_route_set do 171 | xml = <<-XML 172 | 173 | 15 174 | false 175 | true 176 | 2005-03-17 177 | 2005-03-17T21:41:07Z 178 | unparsed 179 | 1 180 | hello 181 | 1974-07-25 182 | 183 | XML 184 | post "/", params: xml, headers: {'CONTENT_TYPE' => 'application/xml'} 185 | 186 | params = @controller.params 187 | assert_equal 15, params[:data][:a] 188 | assert_equal false, params[:data][:b] 189 | assert_equal true, params[:data][:c] 190 | assert_equal Date.new(2005,3,17), params[:data][:d] 191 | assert_equal Time.utc(2005,3,17,21,41,7), params[:data][:e] 192 | assert_equal "unparsed", params[:data][:f] 193 | assert_equal [1, "hello", Date.new(1974,7,25)], params[:data][:g] 194 | end 195 | end 196 | 197 | def test_entities_unescaped_as_xml_simple 198 | with_test_route_set do 199 | xml = <<-XML 200 | <foo "bar's" & friends> 201 | XML 202 | post "/", params: xml, headers: {'CONTENT_TYPE' => 'application/xml'} 203 | assert_equal %(), @controller.params[:data] 204 | end 205 | end 206 | 207 | private 208 | def with_params_parsers(parsers = {}) 209 | old_session = @integration_session 210 | original_parsers = ActionDispatch::Request.parameter_parsers 211 | ActionDispatch::Request.parameter_parsers = original_parsers.merge parsers 212 | reset! 213 | yield 214 | ensure 215 | ActionDispatch::Request.parameter_parsers = original_parsers 216 | @integration_session = old_session 217 | end 218 | 219 | def with_test_route_set 220 | with_routing do |set| 221 | set.draw do 222 | match '/', :to => 'web_service_test/test#assign_parameters', :via => :all 223 | end 224 | yield 225 | end 226 | end 227 | end 228 | -------------------------------------------------------------------------------- /test/xml_params_parsing_test.rb: -------------------------------------------------------------------------------- 1 | require 'helper' 2 | 3 | class XmlParamsParsingTest < ActionDispatch::IntegrationTest 4 | class TestController < ActionController::Base 5 | class << self 6 | attr_accessor :last_request_parameters 7 | attr_accessor :last_request 8 | end 9 | 10 | def parse 11 | self.class.last_request_parameters = request.request_parameters 12 | self.class.last_request = request 13 | head :ok 14 | end 15 | end 16 | 17 | def teardown 18 | TestController.last_request_parameters = nil 19 | TestController.last_request = nil 20 | end 21 | 22 | def assert_parses(expected, xml) 23 | with_test_routing do 24 | post "/parse", params: xml, headers: default_headers 25 | assert_response :ok 26 | assert_equal(expected, TestController.last_request_parameters) 27 | end 28 | end 29 | 30 | test "nils are stripped from collections" do 31 | assert_parses( 32 | {"hash" => { "person" => []} }, 33 | "") 34 | 35 | assert_parses( 36 | {"hash" => { "person" => ['foo']} }, 37 | "foo\n") 38 | end 39 | 40 | test "parses hash params" do 41 | xml = "David" 42 | assert_parses({"person" => {"name" => "David"}}, xml) 43 | end 44 | 45 | test "parses single file" do 46 | xml = "David#{::Base64.encode64('ABC')}" 47 | 48 | person = ActionPack::XmlParser.call(xml) 49 | 50 | assert_equal "image/jpg", person['person']['avatar'].content_type 51 | assert_equal "me.jpg", person['person']['avatar'].original_filename 52 | assert_equal "ABC", person['person']['avatar'].read 53 | end 54 | 55 | test "logs error if parsing unsuccessful" do 56 | with_test_routing do 57 | output = StringIO.new 58 | xml = "David#{::Base64.encode64('ABC')}" 59 | post "/parse", params: xml, headers: default_headers.merge('action_dispatch.show_exceptions' => true, 'action_dispatch.logger' => ActiveSupport::Logger.new(output)) 60 | assert_response :bad_request 61 | output.rewind && err = output.read 62 | assert err =~ /Error occurred while parsing request parameters/ 63 | end 64 | end 65 | 66 | test "occurring a parse error if parsing unsuccessful" do 67 | with_test_routing do 68 | begin 69 | $stderr = StringIO.new # suppress the log 70 | xml = "David" 71 | exception = assert_raise(ActionDispatch::ParamsParser::ParseError) { post "/parse", xml, default_headers.merge('action_dispatch.show_exceptions' => false) } 72 | assert_equal REXML::ParseException, exception.original_exception.class 73 | assert_equal exception.original_exception.message, exception.message 74 | ensure 75 | $stderr = STDERR 76 | end 77 | end 78 | end 79 | 80 | test "parses multiple files" do 81 | xml = <<-end_body 82 | 83 | David 84 | 85 | #{::Base64.encode64('ABC')} 86 | #{::Base64.encode64('DEF')} 87 | 88 | 89 | end_body 90 | 91 | with_test_routing do 92 | post "/parse", params: xml, headers: default_headers 93 | assert_response :ok 94 | end 95 | 96 | person = TestController.last_request_parameters 97 | 98 | assert_equal "image/jpg", person['person']['avatars']['avatar'].first.content_type 99 | assert_equal "me.jpg", person['person']['avatars']['avatar'].first.original_filename 100 | assert_equal "ABC", person['person']['avatars']['avatar'].first.read 101 | 102 | assert_equal "image/gif", person['person']['avatars']['avatar'].last.content_type 103 | assert_equal "you.gif", person['person']['avatars']['avatar'].last.original_filename 104 | assert_equal "DEF", person['person']['avatars']['avatar'].last.read 105 | end 106 | 107 | test "rewinds body if it implements rewind" do 108 | xml = "Marie" 109 | 110 | with_test_routing do 111 | post "/parse", params: xml, headers: default_headers 112 | assert_equal TestController.last_request.body.read, xml 113 | end 114 | end 115 | 116 | private 117 | def with_test_routing 118 | with_routing do |set| 119 | set.draw do 120 | post 'parse', to: 'xml_params_parsing_test/test#parse' 121 | end 122 | yield 123 | end 124 | end 125 | 126 | def default_headers 127 | {'CONTENT_TYPE' => 'application/xml'} 128 | end 129 | end 130 | 131 | class RootLessXmlParamsParsingTest < ActionDispatch::IntegrationTest 132 | class TestController < ActionController::Base 133 | wrap_parameters :person, :format => :xml 134 | 135 | class << self 136 | attr_accessor :last_request_parameters 137 | end 138 | 139 | def parse 140 | self.class.last_request_parameters = request.request_parameters 141 | head :ok 142 | end 143 | end 144 | 145 | def teardown 146 | TestController.last_request_parameters = nil 147 | end 148 | 149 | test "parses hash params" do 150 | with_test_routing do 151 | xml = "David" 152 | post "/parse", params: xml, headers: {'CONTENT_TYPE' => 'application/xml'} 153 | assert_response :ok 154 | assert_equal({"name" => "David", "person" => {"name" => "David"}}, TestController.last_request_parameters) 155 | end 156 | end 157 | 158 | private 159 | def with_test_routing 160 | with_routing do |set| 161 | set.draw do 162 | post 'parse', to: 'root_less_xml_params__parsing_test/test#parse' 163 | end 164 | yield 165 | end 166 | end 167 | end 168 | --------------------------------------------------------------------------------