aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorRafael Mendonça França <rafaelmfranca@gmail.com>2012-11-15 15:50:39 -0200
committerRafael Mendonça França <rafaelmfranca@gmail.com>2012-11-15 15:50:39 -0200
commitcf3eb6dab0e89ea6b64b9bdb24d4df3e8006da7b (patch)
treefd397b746d6ff3cf64931c1671b34d16c43d1088
parenta8c3ea90f1490da4404aa1cea6fc6209f6b9b99b (diff)
parent1fab2002e3385c40ef48008b649d78ce5e16a868 (diff)
downloadrails-cf3eb6dab0e89ea6b64b9bdb24d4df3e8006da7b.tar.gz
rails-cf3eb6dab0e89ea6b64b9bdb24d4df3e8006da7b.tar.bz2
rails-cf3eb6dab0e89ea6b64b9bdb24d4df3e8006da7b.zip
Merge pull request #8219 from nikitug/threadsafe_xmlmini_with_backend
Make XmlMini.with_backend usable with threads Conflicts: activesupport/CHANGELOG.md
-rw-r--r--activesupport/CHANGELOG.md13
-rw-r--r--activesupport/lib/active_support/xml_mini.rb38
-rw-r--r--activesupport/test/xml_mini_test.rb62
3 files changed, 104 insertions, 9 deletions
diff --git a/activesupport/CHANGELOG.md b/activesupport/CHANGELOG.md
index cde98c6667..b55a706b2f 100644
--- a/activesupport/CHANGELOG.md
+++ b/activesupport/CHANGELOG.md
@@ -1,5 +1,18 @@
## Rails 4.0.0 (unreleased) ##
+* `XmlMini.with_backend` now may be safely used with threads:
+
+ Thread.new do
+ XmlMini.with_backend("REXML") { rexml_power }
+ end
+ Thread.new do
+ XmlMini.with_backend("LibXML") { libxml_power }
+ end
+
+ Each thread will use it's own backend.
+
+ *Nikita Afanasenko*
+
* Dependencies no longer trigger Kernel#autoload in remove_constant [fixes #8213]. *Xavier Noria*
* Simplify mocha integration and remove monkey-patches, bumping mocha to 0.13.0. *James Mead*
diff --git a/activesupport/lib/active_support/xml_mini.rb b/activesupport/lib/active_support/xml_mini.rb
index 88f9acb588..d082a0a499 100644
--- a/activesupport/lib/active_support/xml_mini.rb
+++ b/activesupport/lib/active_support/xml_mini.rb
@@ -76,23 +76,24 @@ module ActiveSupport
)
end
- attr_reader :backend
delegate :parse, :to => :backend
+ def backend
+ current_thread_backend || @backend
+ end
+
def backend=(name)
- if name.is_a?(Module)
- @backend = name
- else
- require "active_support/xml_mini/#{name.downcase}"
- @backend = ActiveSupport.const_get("XmlMini_#{name}")
- end
+ backend = name && cast_backend_name_to_module(name)
+ self.current_thread_backend = backend if current_thread_backend
+ @backend = backend
end
def with_backend(name)
- old_backend, self.backend = backend, name
+ old_backend = current_thread_backend
+ self.current_thread_backend = name && cast_backend_name_to_module(name)
yield
ensure
- self.backend = old_backend
+ self.current_thread_backend = old_backend
end
def to_tag(key, value, options)
@@ -163,6 +164,25 @@ module ActiveSupport
f.content_type = entity['content_type']
f
end
+
+ private
+
+ def current_thread_backend
+ Thread.current[:xml_mini_backend]
+ end
+
+ def current_thread_backend=(name)
+ Thread.current[:xml_mini_backend] = name && cast_backend_name_to_module(name)
+ end
+
+ def cast_backend_name_to_module(name)
+ if name.is_a?(Module)
+ name
+ else
+ require "active_support/xml_mini/#{name.downcase}"
+ ActiveSupport.const_get("XmlMini_#{name}")
+ end
+ end
end
XmlMini.backend = 'REXML'
diff --git a/activesupport/test/xml_mini_test.rb b/activesupport/test/xml_mini_test.rb
index 504fc96493..a025279e16 100644
--- a/activesupport/test/xml_mini_test.rb
+++ b/activesupport/test/xml_mini_test.rb
@@ -99,4 +99,66 @@ module XmlMiniTest
end
# TODO: test the remaining functions hidden in #to_tag.
end
+
+ class WithBackendTest < ActiveSupport::TestCase
+ module REXML end
+ module LibXML end
+ module Nokogiri end
+
+ setup do
+ @xml = ActiveSupport::XmlMini
+ end
+
+ test "#with_backend should switch backend and then switch back" do
+ @xml.backend = REXML
+ @xml.with_backend(LibXML) do
+ assert_equal LibXML, @xml.backend
+ @xml.with_backend(Nokogiri) do
+ assert_equal Nokogiri, @xml.backend
+ end
+ assert_equal LibXML, @xml.backend
+ end
+ assert_equal REXML, @xml.backend
+ end
+
+ test "backend switch inside #with_backend block" do
+ @xml.with_backend(LibXML) do
+ @xml.backend = REXML
+ assert_equal REXML, @xml.backend
+ end
+ assert_equal REXML, @xml.backend
+ end
+ end
+
+ class ThreadSafetyTest < ActiveSupport::TestCase
+ module REXML end
+ module LibXML end
+
+ setup do
+ @xml = ActiveSupport::XmlMini
+ end
+
+ test "#with_backend should be thread-safe" do
+ @xml.backend = REXML
+ t = Thread.new do
+ @xml.with_backend(LibXML) { sleep 1 }
+ end
+ sleep 0.1 while t.status != "sleep"
+
+ # We should get `old_backend` here even while another
+ # thread is using `new_backend`.
+ assert_equal REXML, @xml.backend
+ end
+
+ test "nested #with_backend should be thread-safe" do
+ @xml.with_backend(REXML) do
+ t = Thread.new do
+ @xml.with_backend(LibXML) { sleep 1 }
+ end
+ sleep 0.1 while t.status != "sleep"
+
+ assert_equal REXML, @xml.backend
+ end
+ end
+ end
end