diff --git a/actionpack/lib/action_controller/routing/segments.rb b/actionpack/lib/action_controller/routing/segments.rb index f6b03edccace9..1911427d61fba 100644 --- a/actionpack/lib/action_controller/routing/segments.rb +++ b/actionpack/lib/action_controller/routing/segments.rb @@ -191,23 +191,19 @@ def value_regexp end def regexp_chunk - if regexp - if regexp_has_modifiers? - "(#{regexp.to_s})" - else - "(#{regexp.source})" - end - else - "([^#{Routing::SEPARATORS.join}]+)" - end + regexp ? regexp_string : default_regexp_chunk + end + + def regexp_string + regexp_has_modifiers? ? "(#{regexp.to_s})" : "(#{regexp.source})" + end + + def default_regexp_chunk + "([^#{Routing::SEPARATORS.join}]+)" end def number_of_captures - if regexp - regexp.number_of_captures + 1 - else - 1 - end + regexp ? regexp.number_of_captures + 1 : 1 end def build_pattern(pattern) @@ -244,10 +240,6 @@ def regexp_chunk "(?i-:(#{(regexp || Regexp.union(*possible_names)).source}))" end - def number_of_captures - 1 - end - # Don't URI.escape the controller name since it may contain slashes. def interpolation_chunk(value_code = local_name) "\#{#{value_code}.to_s}" @@ -289,8 +281,8 @@ def match_extraction(next_capture) "params[:#{key}] = PathSegment::Result.new_escaped((match[#{next_capture}]#{" || " + default.inspect if default}).split('/'))#{" if match[" + next_capture + "]" if !default}" end - def regexp_chunk - regexp || "(.*)" + def default_regexp_chunk + "(.*)" end def number_of_captures diff --git a/actionpack/lib/action_view/base.rb b/actionpack/lib/action_view/base.rb index f5f3fda571d2c..dbbc159834adf 100644 --- a/actionpack/lib/action_view/base.rb +++ b/actionpack/lib/action_view/base.rb @@ -320,6 +320,8 @@ def _pick_template(template_path) # OPTIMIZE: Checks to lookup template in view path if template = self.view_paths["#{template_file_name}.#{template_format}"] template + elsif template_file_extension && template = self.view_paths["#{template_file_name}.#{template_file_extension}"] + template elsif template = self.view_paths[template_file_name] template elsif (first_render = @_render_stack.first) && first_render.respond_to?(:format_and_extension) && diff --git a/actionpack/test/controller/request_test.rb b/actionpack/test/controller/request_test.rb index 976910c167571..30c28d3e27247 100644 --- a/actionpack/test/controller/request_test.rb +++ b/actionpack/test/controller/request_test.rb @@ -471,6 +471,13 @@ def test_query_string_with_amps ) end + def test_query_string_with_multiple_of_same_name + assert_equal( + { "action" => "update_order", "full_name" => "Lau Taarnskov", "products" => "4" }, + ActionController::AbstractRequest.parse_query_parameters(@query_string_with_multiple_of_same_name) + ) + end + def test_query_string_with_many_equal assert_equal( { "action" => "create_customer", "full_name" => "abc=def=ghi"}, diff --git a/actionpack/test/controller/routing_test.rb b/actionpack/test/controller/routing_test.rb index b8a143cfd9098..2be36d468795f 100644 --- a/actionpack/test/controller/routing_test.rb +++ b/actionpack/test/controller/routing_test.rb @@ -341,6 +341,30 @@ def test_regexp_should_only_match_possible_controllers end end +class PathSegmentTest < Test::Unit::TestCase + def segment(options = {}) + unless @segment + @segment = ROUTING::PathSegment.new(:path, options) + end + @segment + end + + def test_regexp_chunk_should_return_string + segment = segment(:regexp => /[a-z]+/) + assert_kind_of String, segment.regexp_chunk + end + + def test_regexp_chunk_should_be_wrapped_with_parenthesis + segment = segment(:regexp => /[a-z]+/) + assert_equal "([a-z]+)", segment.regexp_chunk + end + + def test_regexp_chunk_should_respect_options + segment = segment(:regexp => /[a-z]+/i) + assert_equal "((?i-mx:[a-z]+))", segment.regexp_chunk + end +end + class RouteBuilderTest < Test::Unit::TestCase def builder @builder ||= ROUTING::RouteBuilder.new @@ -869,6 +893,16 @@ def test_route_with_regexp_for_controller assert_equal '/content/foo', rs.generate(:controller => "content", :action => "foo") end + def test_route_with_regexp_and_captures_for_controller + rs.draw do |map| + map.connect ':controller/:action/:id', :controller => /admin\/(accounts|users)/ + end + assert_equal({:controller => "admin/accounts", :action => "index"}, rs.recognize_path("/admin/accounts")) + assert_equal({:controller => "admin/users", :action => "index"}, rs.recognize_path("/admin/users")) + assert_raises(ActionController::RoutingError) { rs.recognize_path("/admin/products") } + end + + def test_route_with_regexp_and_dot rs.draw do |map| map.connect ':controller/:action/:file', @@ -1149,6 +1183,7 @@ def test_route_with_text_default assert_equal({:controller => "content", :action => 'show_page', :id => 'foo'}, rs.recognize_path("/page/foo")) token = "\321\202\320\265\320\272\321\201\321\202" # 'text' in russian + token.force_encoding("UTF-8") if token.respond_to?(:force_encoding) escaped_token = CGI::escape(token) assert_equal '/page/' + escaped_token, rs.generate(:controller => 'content', :action => 'show_page', :id => token) diff --git a/actionpack/test/fixtures/test/hello_world.js b/actionpack/test/fixtures/test/hello_world.js new file mode 100644 index 0000000000000..2f3f65f2265aa --- /dev/null +++ b/actionpack/test/fixtures/test/hello_world.js @@ -0,0 +1 @@ +var greeting = 'Hallo World!'; \ No newline at end of file diff --git a/actionpack/test/template/render_test.rb b/actionpack/test/template/render_test.rb index 531df6ce64914..3872ebfd3947b 100644 --- a/actionpack/test/template/render_test.rb +++ b/actionpack/test/template/render_test.rb @@ -34,6 +34,10 @@ def test_render_file_with_full_path assert_equal "Hello world!", @view.render(:file => template_path) end + def test_render_file_not_using_template_handler_extension + assert_equal "var greeting = 'Hallo World!';", @view.render(:file => 'test/hello_world.js') + end + def test_render_file_with_instance_variables assert_deprecated do assert_equal "The secret is in the sauce\n", @view.render("test/render_file_with_ivar.erb") diff --git a/activesupport/CHANGELOG b/activesupport/CHANGELOG index f52d5724ef032..d027d7befbd64 100644 --- a/activesupport/CHANGELOG +++ b/activesupport/CHANGELOG @@ -1,5 +1,7 @@ *2.2.3 (next release)* +* Ruby 1.9.1p0 fix: URI.unescape can decode multibyte chars. #2033 [MOROHASHI Kyosuke] + * TimeWithZone#xmlschema accepts optional fraction_digits argument [#1725 state:resolved] [Nicholas Dainty] * TimeWithZone#- gives correct result with wrapped DateTime, and with DateTime argument [Geoff Buesing] diff --git a/activesupport/lib/active_support/core_ext/file/atomic.rb b/activesupport/lib/active_support/core_ext/file/atomic.rb index f988eff3d9c9b..066be80012705 100644 --- a/activesupport/lib/active_support/core_ext/file/atomic.rb +++ b/activesupport/lib/active_support/core_ext/file/atomic.rb @@ -27,7 +27,7 @@ def atomic_write(file_name, temp_dir = Dir.tmpdir) old_stat = stat(file_name) rescue Errno::ENOENT # No old permissions, write a temp file to determine the defaults - check_name = ".permissions_check.#{Thread.current.object_id}.#{Process.pid}.#{rand(1000000)}" + check_name = join(dirname(file_name), ".permissions_check.#{Thread.current.object_id}.#{Process.pid}.#{rand(1000000)}") open(check_name, "w") { } old_stat = stat(check_name) unlink(check_name) diff --git a/activesupport/lib/active_support/core_ext/uri.rb b/activesupport/lib/active_support/core_ext/uri.rb new file mode 100644 index 0000000000000..9a1c61d99b59a --- /dev/null +++ b/activesupport/lib/active_support/core_ext/uri.rb @@ -0,0 +1,16 @@ +if RUBY_VERSION >= '1.9' + require 'uri' + + str = "\xE6\x97\xA5\xE6\x9C\xAC\xE8\xAA\x9E" # Ni-ho-nn-go in UTF-8, means Japanese. + str.force_encoding(Encoding::UTF_8) if str.respond_to?(:force_encoding) + + unless str == URI.unescape(URI.escape(str)) + URI::Parser.class_eval do + remove_method :unescape + def unescape(str, escaped = @regexp[:ESCAPED]) + enc = (str.encoding == Encoding::US_ASCII) ? Encoding::UTF_8 : str.encoding + str.gsub(escaped) { [$&[1, 2].hex].pack('C') }.force_encoding(enc) + end + end + end +end diff --git a/activesupport/lib/active_support/inflector.rb b/activesupport/lib/active_support/inflector.rb index ad2660e6c854c..00810a0e94d6b 100644 --- a/activesupport/lib/active_support/inflector.rb +++ b/activesupport/lib/active_support/inflector.rb @@ -277,7 +277,7 @@ def transliterate(string) # The iconv transliteration code doesn't function correctly # on some platforms, but it's very fast where it does function. - if "foo" != Inflector.transliterate("föö") + if "foo" != (Inflector.transliterate("föö") rescue nil) undef_method :transliterate def transliterate(string) string.mb_chars.normalize(:kd). # Decompose accented characters diff --git a/activesupport/lib/active_support/json/decoding.rb b/activesupport/lib/active_support/json/decoding.rb index fdb219dbf77dc..5274600bfe5b6 100644 --- a/activesupport/lib/active_support/json/decoding.rb +++ b/activesupport/lib/active_support/json/decoding.rb @@ -46,10 +46,11 @@ def convert_json_to_yaml(json) #:nodoc: json.gsub(/\\\//, '/') else left_pos = [-1].push(*marks) - right_pos = marks << json.length + right_pos = marks << scanner.pos + scanner.rest_size output = [] left_pos.each_with_index do |left, i| - output << json[left.succ..right_pos[i]] + scanner.pos = left.succ + output << scanner.peek(right_pos[i] - scanner.pos + 1) end output = output * " " diff --git a/activesupport/lib/active_support/vendor.rb b/activesupport/lib/active_support/vendor.rb index 23ceeb1359f68..e46ccd48417f8 100644 --- a/activesupport/lib/active_support/vendor.rb +++ b/activesupport/lib/active_support/vendor.rb @@ -14,7 +14,7 @@ end begin - gem 'memcache-client', '~> 1.5.1' + gem 'memcache-client', '>= 1.5.1' rescue Gem::LoadError $:.unshift "#{File.dirname(__FILE__)}/vendor/memcache-client-1.5.1" end diff --git a/activesupport/test/core_ext/uri_ext_test.rb b/activesupport/test/core_ext/uri_ext_test.rb new file mode 100644 index 0000000000000..0837d3cb2daa9 --- /dev/null +++ b/activesupport/test/core_ext/uri_ext_test.rb @@ -0,0 +1,12 @@ +require 'abstract_unit' +require 'uri' + +class URIExtTest < Test::Unit::TestCase + def test_uri_decode_handle_multibyte + str = "\xE6\x97\xA5\xE6\x9C\xAC\xE8\xAA\x9E" # Ni-ho-nn-go in UTF-8, means Japanese. + str.force_encoding(Encoding::UTF_8) if str.respond_to?(:force_encoding) + + assert_equal str, URI.unescape(URI.escape(str)) + assert_equal str, URI.decode(URI.escape(str)) + end +end diff --git a/activesupport/test/json/decoding_test.rb b/activesupport/test/json/decoding_test.rb index 19ae3a01a8e68..72efb1f139bd2 100644 --- a/activesupport/test/json/decoding_test.rb +++ b/activesupport/test/json/decoding_test.rb @@ -1,3 +1,4 @@ +# encoding: UTF-8 require 'abstract_unit' class TestJSONDecoding < Test::Unit::TestCase @@ -10,6 +11,8 @@ class TestJSONDecoding < Test::Unit::TestCase %({"returnTo":[1,"\\"a\\",", "b"]}) => {"returnTo" => [1, "\"a\",", "b"]}, %({a: "'", "b": "5,000"}) => {"a" => "'", "b" => "5,000"}, %({a: "a's, b's and c's", "b": "5,000"}) => {"a" => "a's, b's and c's", "b" => "5,000"}, + # multibyte + %({"matzue": "松江", "asakusa": "浅草"}) => {"matzue" => "松江", "asakusa" => "浅草"}, %({a: "2007-01-01"}) => {'a' => Date.new(2007, 1, 1)}, %({a: "2007-01-01 01:12:34 Z"}) => {'a' => Time.utc(2007, 1, 1, 1, 12, 34)}, # no time zone diff --git a/ci/geminstaller.yml b/ci/geminstaller.yml index 4251518999570..d11bf04485108 100644 --- a/ci/geminstaller.yml +++ b/ci/geminstaller.yml @@ -11,8 +11,8 @@ gems: - name: mysql #version: >= 2.7 version: = 2.7 -- name: postgres - version: >= 0.7.9.2008.01.28 +- name: pg + version: >= 0.7.9.2008.10.13 - name: rack version: '~> 0.9.0' - name: rake