aboutsummaryrefslogtreecommitdiffstats
path: root/activesupport
diff options
context:
space:
mode:
Diffstat (limited to 'activesupport')
-rw-r--r--activesupport/CHANGELOG7
-rw-r--r--activesupport/lib/active_support/configurable.rb37
-rw-r--r--activesupport/lib/active_support/core_ext/kernel/reporting.rb3
-rw-r--r--activesupport/lib/active_support/core_ext/uri.rb3
-rw-r--r--activesupport/lib/active_support/hash_with_indifferent_access.rb6
-rw-r--r--activesupport/lib/active_support/json/encoding.rb24
-rw-r--r--activesupport/lib/active_support/multibyte/chars.rb4
-rw-r--r--activesupport/lib/active_support/notifications/fanout.rb20
-rw-r--r--activesupport/lib/active_support/testing/isolation.rb3
-rw-r--r--activesupport/test/configurable_test.rb42
-rw-r--r--activesupport/test/core_ext/hash_ext_test.rb41
-rw-r--r--activesupport/test/core_ext/uri_ext_test.rb2
-rw-r--r--activesupport/test/json/encoding_test.rb21
-rw-r--r--activesupport/test/multibyte_chars_test.rb2
-rw-r--r--activesupport/test/notifications_test.rb22
15 files changed, 197 insertions, 40 deletions
diff --git a/activesupport/CHANGELOG b/activesupport/CHANGELOG
index da370303c6..c47839b001 100644
--- a/activesupport/CHANGELOG
+++ b/activesupport/CHANGELOG
@@ -1,5 +1,12 @@
+*Rails 3.0.0 [beta 4/release candidate] (unreleased)*
+
+* JSON: encode objects that don't have a native JSON representation using to_hash, if available, instead of instance_values (the old fallback) or to_s (other encoders' default). Encode BigDecimal and Regexp encode as strings to conform with other encoders. Try to transcode non-UTF-8 strings. [Jeremy Kemper]
+
+
*Rails 3.0.0 [beta 3] (April 13th, 2010)*
+* HashWithIndifferentAccess: remove inherited symbolize_keys! since its keys are always strings. [Santiago Pastorino]
+
* Improve transliteration quality. #4374 [Norman Clarke]
* Speed up and add Ruby 1.9 support for ActiveSupport::Multibyte::Chars#tidy_bytes. #4350 [Norman Clarke]
diff --git a/activesupport/lib/active_support/configurable.rb b/activesupport/lib/active_support/configurable.rb
index 890f465ce1..f562e17c75 100644
--- a/activesupport/lib/active_support/configurable.rb
+++ b/activesupport/lib/active_support/configurable.rb
@@ -1,35 +1,36 @@
-require "active_support/concern"
+require 'active_support/concern'
+require 'active_support/ordered_options'
+require 'active_support/core_ext/kernel/singleton_class'
+require 'active_support/core_ext/module/delegation'
module ActiveSupport
module Configurable
extend ActiveSupport::Concern
module ClassMethods
- def get_config
- module_parts = name.split("::")
- modules = [Object]
- module_parts.each {|name| modules.push modules.last.const_get(name) }
- modules.reverse_each do |mod|
- return mod.const_get(:DEFAULT_CONFIG) if const_defined?(:DEFAULT_CONFIG)
- end
- {}
- end
-
def config
- self.config = get_config unless @config
- @config
+ @config ||= ActiveSupport::InheritableOptions.new(superclass.respond_to?(:config) ? superclass.config : {})
end
- def config=(hash)
- @config = ActiveSupport::OrderedOptions.new
- hash.each do |key, value|
- @config[key] = value
+ def configure
+ yield config
+ end
+
+ def config_accessor(*names)
+ names.each do |name|
+ code, line = <<-RUBY, __LINE__ + 1
+ def #{name}; config.#{name}; end
+ def #{name}=(value); config.#{name} = value; end
+ RUBY
+
+ singleton_class.class_eval code, __FILE__, line
+ class_eval code, __FILE__, line
end
end
end
def config
- self.class.config
+ @config ||= ActiveSupport::InheritableOptions.new(self.class.config)
end
end
end \ No newline at end of file
diff --git a/activesupport/lib/active_support/core_ext/kernel/reporting.rb b/activesupport/lib/active_support/core_ext/kernel/reporting.rb
index ac35db6ab6..7c455f66d5 100644
--- a/activesupport/lib/active_support/core_ext/kernel/reporting.rb
+++ b/activesupport/lib/active_support/core_ext/kernel/reporting.rb
@@ -1,3 +1,4 @@
+require 'rbconfig'
module Kernel
# Sets $VERBOSE to nil for the duration of the block and back to its original value afterwards.
#
@@ -37,7 +38,7 @@ module Kernel
# puts 'But this will'
def silence_stream(stream)
old_stream = stream.dup
- stream.reopen(RUBY_PLATFORM =~ /mswin|mingw/ ? 'NUL:' : '/dev/null')
+ stream.reopen(Config::CONFIG['host_os'] =~ /mswin|mingw/ ? 'NUL:' : '/dev/null')
stream.sync = true
yield
ensure
diff --git a/activesupport/lib/active_support/core_ext/uri.rb b/activesupport/lib/active_support/core_ext/uri.rb
index ca1be8b7e9..28eabd2111 100644
--- a/activesupport/lib/active_support/core_ext/uri.rb
+++ b/activesupport/lib/active_support/core_ext/uri.rb
@@ -1,8 +1,9 @@
+# encoding: utf-8
+
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)
parser = URI::Parser.new
unless str == parser.unescape(parser.escape(str))
diff --git a/activesupport/lib/active_support/hash_with_indifferent_access.rb b/activesupport/lib/active_support/hash_with_indifferent_access.rb
index 8241b69c8b..f64f0f44cc 100644
--- a/activesupport/lib/active_support/hash_with_indifferent_access.rb
+++ b/activesupport/lib/active_support/hash_with_indifferent_access.rb
@@ -1,3 +1,5 @@
+require 'active_support/core_ext/hash/keys'
+
# This class has dubious semantics and we only have it so that
# people can write params[:key] instead of params['key']
# and they get the same value for both keys.
@@ -112,7 +114,9 @@ module ActiveSupport
end
def stringify_keys!; self end
- def symbolize_keys!; self end
+ def stringify_keys; dup end
+ undef :symbolize_keys!
+ def symbolize_keys; to_hash.symbolize_keys end
def to_options!; self end
# Convert to a Hash with String keys.
diff --git a/activesupport/lib/active_support/json/encoding.rb b/activesupport/lib/active_support/json/encoding.rb
index 8ba45f7ea2..0f38fd0e89 100644
--- a/activesupport/lib/active_support/json/encoding.rb
+++ b/activesupport/lib/active_support/json/encoding.rb
@@ -1,4 +1,5 @@
# encoding: utf-8
+require 'bigdecimal'
require 'active_support/core_ext/array/wrap'
require 'active_support/core_ext/hash/except'
require 'active_support/core_ext/hash/slice'
@@ -102,7 +103,9 @@ module ActiveSupport
end
def escape(string)
- string = string.dup.force_encoding(::Encoding::BINARY) if string.respond_to?(:force_encoding)
+ if string.respond_to?(:force_encoding)
+ string = string.encode(::Encoding::UTF_8, undef: :replace).force_encoding(::Encoding::BINARY)
+ end
json = string.
gsub(escape_regex) { |s| ESCAPED_CHARS[s] }.
gsub(/([\xC0-\xDF][\x80-\xBF]|
@@ -110,7 +113,9 @@ module ActiveSupport
[\xF0-\xF7][\x80-\xBF]{3})+/nx) { |s|
s.unpack("U*").pack("n*").unpack("H*")[0].gsub(/.{4}/n, '\\\\u\&')
}
- %("#{json}")
+ json = %("#{json}")
+ json.force_encoding(::Encoding::UTF_8) if json.respond_to?(:force_encoding)
+ json
end
end
@@ -128,7 +133,13 @@ class Object
ActiveSupport::JSON.encode(self, options)
end
- def as_json(options = nil) instance_values end #:nodoc:
+ def as_json(options = nil) #:nodoc:
+ if respond_to?(:to_hash)
+ to_hash
+ else
+ instance_values
+ end
+ end
end
# A string that returns itself as its JSON-encoded form.
@@ -166,9 +177,12 @@ class Numeric
def encode_json(encoder) to_s end #:nodoc:
end
+class BigDecimal
+ def as_json(options = nil) to_s end #:nodoc:
+end
+
class Regexp
- def as_json(options = nil) self end #:nodoc:
- def encode_json(encoder) inspect end #:nodoc:
+ def as_json(options = nil) to_s end #:nodoc:
end
module Enumerable
diff --git a/activesupport/lib/active_support/multibyte/chars.rb b/activesupport/lib/active_support/multibyte/chars.rb
index 38007fd4e7..4ade1158fd 100644
--- a/activesupport/lib/active_support/multibyte/chars.rb
+++ b/activesupport/lib/active_support/multibyte/chars.rb
@@ -72,8 +72,8 @@ module ActiveSupport #:nodoc:
def self.codepoints_to_pattern(array_of_codepoints) #:nodoc:
array_of_codepoints.collect{ |e| [e].pack 'U*' }.join('|')
end
- UNICODE_TRAILERS_PAT = /(#{codepoints_to_pattern(UNICODE_LEADERS_AND_TRAILERS)})+\Z/
- UNICODE_LEADERS_PAT = /\A(#{codepoints_to_pattern(UNICODE_LEADERS_AND_TRAILERS)})+/
+ UNICODE_TRAILERS_PAT = /(#{codepoints_to_pattern(UNICODE_LEADERS_AND_TRAILERS)})+\Z/u
+ UNICODE_LEADERS_PAT = /\A(#{codepoints_to_pattern(UNICODE_LEADERS_AND_TRAILERS)})+/u
UTF8_PAT = ActiveSupport::Multibyte::VALID_CHARACTER['UTF-8']
diff --git a/activesupport/lib/active_support/notifications/fanout.rb b/activesupport/lib/active_support/notifications/fanout.rb
index a3ddc7705a..300ec842a9 100644
--- a/activesupport/lib/active_support/notifications/fanout.rb
+++ b/activesupport/lib/active_support/notifications/fanout.rb
@@ -19,8 +19,8 @@ module ActiveSupport
end
def unsubscribe(subscriber)
- @subscribers.delete(subscriber)
@listeners_for.clear
+ @subscribers.reject! {|s| s.matches?(subscriber)}
end
def publish(name, *args)
@@ -60,7 +60,7 @@ module ActiveSupport
end
def publish(*args)
- return unless matches?(args.first)
+ return unless subscribed_to?(args.first)
push(*args)
true
end
@@ -69,10 +69,20 @@ module ActiveSupport
true
end
- private
- def matches?(name)
- !@pattern || @pattern =~ name.to_s
+ def subscribed_to?(name)
+ !@pattern || @pattern =~ name.to_s
+ end
+
+ def matches?(subscriber_or_name)
+ case subscriber_or_name
+ when String
+ @pattern && @pattern =~ subscriber_or_name
+ when self
+ true
end
+ end
+
+ private
def push(*args)
@block.call(*args)
diff --git a/activesupport/lib/active_support/testing/isolation.rb b/activesupport/lib/active_support/testing/isolation.rb
index 9507dbf473..69df399cde 100644
--- a/activesupport/lib/active_support/testing/isolation.rb
+++ b/activesupport/lib/active_support/testing/isolation.rb
@@ -1,3 +1,4 @@
+require 'rbconfig'
module ActiveSupport
module Testing
class RemoteError < StandardError
@@ -33,7 +34,7 @@ module ActiveSupport
module Isolation
def self.forking_env?
- !ENV["NO_FORK"] && RUBY_PLATFORM !~ /mswin|mingw|java/
+ !ENV["NO_FORK"] && ((Config::CONFIG['host_os'] !~ /mswin|mingw/) && (RUBY_PLATFORM !~ /java/))
end
def self.included(base)
diff --git a/activesupport/test/configurable_test.rb b/activesupport/test/configurable_test.rb
new file mode 100644
index 0000000000..cef67e3cf9
--- /dev/null
+++ b/activesupport/test/configurable_test.rb
@@ -0,0 +1,42 @@
+require 'abstract_unit'
+require 'active_support/configurable'
+
+class ConfigurableActiveSupport < ActiveSupport::TestCase
+ class Parent
+ include ActiveSupport::Configurable
+ config_accessor :foo
+ end
+
+ class Child < Parent
+ end
+
+ setup do
+ Parent.config.clear
+ Parent.config.foo = :bar
+
+ Child.config.clear
+ end
+
+ test "adds a configuration hash" do
+ assert_equal({ :foo => :bar }, Parent.config)
+ end
+
+ test "configuration hash is inheritable" do
+ assert_equal :bar, Child.config.foo
+ assert_equal :bar, Parent.config.foo
+
+ Child.config.foo = :baz
+ assert_equal :baz, Child.config.foo
+ assert_equal :bar, Parent.config.foo
+ end
+
+ test "configuration hash is available on instance" do
+ instance = Parent.new
+ assert_equal :bar, instance.config.foo
+ assert_equal :bar, Parent.config.foo
+
+ instance.config.foo = :baz
+ assert_equal :baz, instance.config.foo
+ assert_equal :bar, Parent.config.foo
+ end
+end \ No newline at end of file
diff --git a/activesupport/test/core_ext/hash_ext_test.rb b/activesupport/test/core_ext/hash_ext_test.rb
index 86272a28c1..b2a9731578 100644
--- a/activesupport/test/core_ext/hash_ext_test.rb
+++ b/activesupport/test/core_ext/hash_ext_test.rb
@@ -60,6 +60,43 @@ class HashExtTest < Test::Unit::TestCase
assert_equal @strings, @mixed.dup.stringify_keys!
end
+ def test_symbolize_keys_for_hash_with_indifferent_access
+ assert_instance_of Hash, @symbols.with_indifferent_access.symbolize_keys
+ assert_equal @symbols, @symbols.with_indifferent_access.symbolize_keys
+ assert_equal @symbols, @strings.with_indifferent_access.symbolize_keys
+ assert_equal @symbols, @mixed.with_indifferent_access.symbolize_keys
+ end
+
+ def test_symbolize_keys_bang_for_hash_with_indifferent_access
+ assert_raise(NoMethodError) { @symbols.with_indifferent_access.dup.symbolize_keys! }
+ assert_raise(NoMethodError) { @strings.with_indifferent_access.dup.symbolize_keys! }
+ assert_raise(NoMethodError) { @mixed.with_indifferent_access.dup.symbolize_keys! }
+ end
+
+ def test_symbolize_keys_preserves_keys_that_cant_be_symbolized_for_hash_with_indifferent_access
+ assert_equal @illegal_symbols, @illegal_symbols.with_indifferent_access.symbolize_keys
+ assert_raise(NoMethodError) { @illegal_symbols.with_indifferent_access.dup.symbolize_keys! }
+ end
+
+ def test_symbolize_keys_preserves_fixnum_keys_for_hash_with_indifferent_access
+ assert_equal @fixnums, @fixnums.with_indifferent_access.symbolize_keys
+ assert_raise(NoMethodError) { @fixnums.with_indifferent_access.dup.symbolize_keys! }
+ end
+
+ def test_stringify_keys_for_hash_with_indifferent_access
+ assert_instance_of ActiveSupport::HashWithIndifferentAccess, @symbols.with_indifferent_access.stringify_keys
+ assert_equal @strings, @symbols.with_indifferent_access.stringify_keys
+ assert_equal @strings, @strings.with_indifferent_access.stringify_keys
+ assert_equal @strings, @mixed.with_indifferent_access.stringify_keys
+ end
+
+ def test_stringify_keys_bang_for_hash_with_indifferent_access
+ assert_instance_of ActiveSupport::HashWithIndifferentAccess, @symbols.with_indifferent_access.dup.stringify_keys!
+ assert_equal @strings, @symbols.with_indifferent_access.dup.stringify_keys!
+ assert_equal @strings, @strings.with_indifferent_access.dup.stringify_keys!
+ assert_equal @strings, @mixed.with_indifferent_access.dup.stringify_keys!
+ end
+
def test_indifferent_assorted
@strings = @strings.with_indifferent_access
@symbols = @symbols.with_indifferent_access
@@ -213,11 +250,11 @@ class HashExtTest < Test::Unit::TestCase
def test_stringify_and_symbolize_keys_on_indifferent_preserves_hash
h = HashWithIndifferentAccess.new
h[:first] = 1
- h.stringify_keys!
+ h = h.stringify_keys
assert_equal 1, h['first']
h = HashWithIndifferentAccess.new
h['first'] = 1
- h.symbolize_keys!
+ h = h.symbolize_keys
assert_equal 1, h[:first]
end
diff --git a/activesupport/test/core_ext/uri_ext_test.rb b/activesupport/test/core_ext/uri_ext_test.rb
index e4a242abc4..d988837603 100644
--- a/activesupport/test/core_ext/uri_ext_test.rb
+++ b/activesupport/test/core_ext/uri_ext_test.rb
@@ -1,3 +1,4 @@
+# encoding: utf-8
require 'abstract_unit'
require 'uri'
require 'active_support/core_ext/uri'
@@ -5,7 +6,6 @@ require 'active_support/core_ext/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)
if URI.const_defined?(:Parser)
parser = URI::Parser.new
diff --git a/activesupport/test/json/encoding_test.rb b/activesupport/test/json/encoding_test.rb
index 188b799f3f..ff95c0ca18 100644
--- a/activesupport/test/json/encoding_test.rb
+++ b/activesupport/test/json/encoding_test.rb
@@ -9,6 +9,12 @@ class TestJSONEncoding < Test::Unit::TestCase
end
end
+ class Hashlike
+ def to_hash
+ { :a => 1 }
+ end
+ end
+
class Custom
def as_json(options)
'custom'
@@ -19,7 +25,8 @@ class TestJSONEncoding < Test::Unit::TestCase
FalseTests = [[ false, %(false) ]]
NilTests = [[ nil, %(null) ]]
NumericTests = [[ 1, %(1) ],
- [ 2.5, %(2.5) ]]
+ [ 2.5, %(2.5) ],
+ [ BigDecimal('2.5'), %("#{BigDecimal('2.5').to_s}") ]]
StringTests = [[ 'this is the <string>', %("this is the \\u003Cstring\\u003E")],
[ 'a "string" with quotes & an ampersand', %("a \\"string\\" with quotes \\u0026 an ampersand") ],
@@ -35,11 +42,12 @@ class TestJSONEncoding < Test::Unit::TestCase
[ :"a b", %("a b") ]]
ObjectTests = [[ Foo.new(1, 2), %({\"a\":1,\"b\":2}) ]]
+ HashlikeTests = [[ Hashlike.new, %({\"a\":1}) ]]
CustomTests = [[ Custom.new, '"custom"' ]]
VariableTests = [[ ActiveSupport::JSON::Variable.new('foo'), 'foo'],
[ ActiveSupport::JSON::Variable.new('alert("foo")'), 'alert("foo")']]
- RegexpTests = [[ /^a/, '/^a/' ], [/^\w{1,2}[a-z]+/ix, '/^\\w{1,2}[a-z]+/ix']]
+ RegexpTests = [[ /^a/, '"(?-mix:^a)"' ], [/^\w{1,2}[a-z]+/ix, '"(?ix-m:^\\\\w{1,2}[a-z]+)"']]
DateTests = [[ Date.new(2005,2,1), %("2005/02/01") ]]
TimeTests = [[ Time.utc(2005,2,1,15,15,10), %("2005/02/01 15:15:10 +0000") ]]
@@ -91,6 +99,15 @@ class TestJSONEncoding < Test::Unit::TestCase
end
end
+ if '1.9'.respond_to?(:force_encoding)
+ def test_non_utf8_string_transcodes
+ s = '二'.encode('Shift_JIS')
+ result = ActiveSupport::JSON.encode(s)
+ assert_equal '"\\u4e8c"', result
+ assert_equal Encoding::UTF_8, result.encoding
+ end
+ end
+
def test_exception_raised_when_encoding_circular_reference
a = [1]
a << a
diff --git a/activesupport/test/multibyte_chars_test.rb b/activesupport/test/multibyte_chars_test.rb
index 1b8d13c024..caf50aa1c9 100644
--- a/activesupport/test/multibyte_chars_test.rb
+++ b/activesupport/test/multibyte_chars_test.rb
@@ -105,7 +105,7 @@ class MultibyteCharsUTF8BehaviourTest < Test::Unit::TestCase
@whitespace = "\n\t#{[32, 8195].pack('U*')}"
else
# Ruby 1.9 only supports basic whitespace
- @whitespace = "\n\t ".force_encoding(Encoding::UTF_8)
+ @whitespace = "\n\t "
end
@byte_order_mark = [65279].pack('U')
diff --git a/activesupport/test/notifications_test.rb b/activesupport/test/notifications_test.rb
index 92fbe5b92f..c2e1c588f0 100644
--- a/activesupport/test/notifications_test.rb
+++ b/activesupport/test/notifications_test.rb
@@ -6,7 +6,9 @@ module Notifications
ActiveSupport::Notifications.notifier = nil
@notifier = ActiveSupport::Notifications.notifier
@events = []
+ @named_events = []
@subscription = @notifier.subscribe { |*args| @events << event(*args) }
+ @named_subscription = @notifier.subscribe("named.subscription") { |*args| @named_events << event(*args) }
end
private
@@ -30,6 +32,26 @@ module Notifications
assert_equal [[:foo]], @events
end
+ def test_unsubscribing_by_name_removes_a_subscription
+ @notifier.publish "named.subscription", :foo
+ @notifier.wait
+ assert_equal [["named.subscription", :foo]], @named_events
+ @notifier.unsubscribe("named.subscription")
+ @notifier.publish "named.subscription", :foo
+ @notifier.wait
+ assert_equal [["named.subscription", :foo]], @named_events
+ end
+
+ def test_unsubscribing_by_name_leaves_the_other_subscriptions
+ @notifier.publish "named.subscription", :foo
+ @notifier.wait
+ assert_equal [["named.subscription", :foo]], @events
+ @notifier.unsubscribe("named.subscription")
+ @notifier.publish "named.subscription", :foo
+ @notifier.wait
+ assert_equal [["named.subscription", :foo], ["named.subscription", :foo]], @events
+ end
+
private
def event(*args)
args