diff options
Diffstat (limited to 'activesupport/lib/active_support/core_ext')
4 files changed, 150 insertions, 10 deletions
diff --git a/activesupport/lib/active_support/core_ext/module.rb b/activesupport/lib/active_support/core_ext/module.rb index b4efff8b24..ef038331c2 100644 --- a/activesupport/lib/active_support/core_ext/module.rb +++ b/activesupport/lib/active_support/core_ext/module.rb @@ -3,6 +3,7 @@ require 'active_support/core_ext/module/introspection' require 'active_support/core_ext/module/anonymous' require 'active_support/core_ext/module/reachable' require 'active_support/core_ext/module/attribute_accessors' +require 'active_support/core_ext/module/attribute_accessors_per_thread' require 'active_support/core_ext/module/attr_internal' require 'active_support/core_ext/module/concerning' require 'active_support/core_ext/module/delegation' diff --git a/activesupport/lib/active_support/core_ext/module/attribute_accessors.rb b/activesupport/lib/active_support/core_ext/module/attribute_accessors.rb index 124f90dc0f..76825862d7 100644 --- a/activesupport/lib/active_support/core_ext/module/attribute_accessors.rb +++ b/activesupport/lib/active_support/core_ext/module/attribute_accessors.rb @@ -49,7 +49,7 @@ class Module # include HairColors # end # - # Person.hair_colors # => [:brown, :black, :blonde, :red] + # Person.new.hair_colors # => [:brown, :black, :blonde, :red] def mattr_reader(*syms) options = syms.extract_options! syms.each do |sym| @@ -105,7 +105,7 @@ class Module # # Also, you can pass a block to set up the attribute with a default value. # - # class HairColors + # module HairColors # mattr_writer :hair_colors do # [:brown, :black, :blonde, :red] # end @@ -150,8 +150,8 @@ class Module # include HairColors # end # - # Person.hair_colors = [:brown, :black, :blonde, :red] - # Person.hair_colors # => [:brown, :black, :blonde, :red] + # HairColors.hair_colors = [:brown, :black, :blonde, :red] + # HairColors.hair_colors # => [:brown, :black, :blonde, :red] # Person.new.hair_colors # => [:brown, :black, :blonde, :red] # # If a subclass changes the value then that would also change the value for @@ -161,8 +161,8 @@ class Module # class Male < Person # end # - # Male.hair_colors << :blue - # Person.hair_colors # => [:brown, :black, :blonde, :red, :blue] + # Male.new.hair_colors << :blue + # Person.new.hair_colors # => [:brown, :black, :blonde, :red, :blue] # # To opt out of the instance writer method, pass <tt>instance_writer: false</tt>. # To opt out of the instance reader method, pass <tt>instance_reader: false</tt>. diff --git a/activesupport/lib/active_support/core_ext/module/attribute_accessors_per_thread.rb b/activesupport/lib/active_support/core_ext/module/attribute_accessors_per_thread.rb new file mode 100644 index 0000000000..8a7e6776da --- /dev/null +++ b/activesupport/lib/active_support/core_ext/module/attribute_accessors_per_thread.rb @@ -0,0 +1,141 @@ +require 'active_support/core_ext/array/extract_options' + +# Extends the module object with class/module and instance accessors for +# class/module attributes, just like the native attr* accessors for instance +# attributes, but does so on a per-thread basis. +# +# So the values are scoped within the Thread.current space under the class name +# of the module. +class Module + # Defines a per-thread class attribute and creates class and instance reader methods. + # The underlying per-thread class variable is set to +nil+, if it is not previously defined. + # + # module Current + # thread_mattr_reader :user + # end + # + # Current.user # => nil + # Thread.current[:attr_Current_user] = "DHH" + # Current.user # => "DHH" + # + # The attribute name must be a valid method name in Ruby. + # + # module Foo + # thread_mattr_reader :"1_Badname" + # end + # # => NameError: invalid attribute name: 1_Badname + # + # If you want to opt out the creation on the instance reader method, pass + # <tt>instance_reader: false</tt> or <tt>instance_accessor: false</tt>. + # + # class Current + # thread_mattr_reader :user, instance_reader: false + # end + # + # Current.new.user # => NoMethodError + def thread_mattr_reader(*syms) + options = syms.extract_options! + + syms.each do |sym| + raise NameError.new("invalid attribute name: #{sym}") unless sym =~ /^[_A-Za-z]\w*$/ + class_eval(<<-EOS, __FILE__, __LINE__ + 1) + def self.#{sym} + Thread.current[:"attr_#{name}_#{sym}"] + end + EOS + + unless options[:instance_reader] == false || options[:instance_accessor] == false + class_eval(<<-EOS, __FILE__, __LINE__ + 1) + def #{sym} + Thread.current[:"attr_#{self.class.name}_#{sym}"] + end + EOS + end + end + end + alias :thread_cattr_reader :thread_mattr_reader + + # Defines a per-thread class attribute and creates a class and instance writer methods to + # allow assignment to the attribute. + # + # module Current + # thread_mattr_writer :user + # end + # + # Current.user = "DHH" + # Thread.current[:attr_Current_user] # => "DHH" + # + # If you want to opt out the instance writer method, pass + # <tt>instance_writer: false</tt> or <tt>instance_accessor: false</tt>. + # + # class Current + # thread_mattr_writer :user, instance_writer: false + # end + # + # Current.new.user = "DHH" # => NoMethodError + def thread_mattr_writer(*syms) + options = syms.extract_options! + syms.each do |sym| + raise NameError.new("invalid attribute name: #{sym}") unless sym =~ /^[_A-Za-z]\w*$/ + class_eval(<<-EOS, __FILE__, __LINE__ + 1) + def self.#{sym}=(obj) + Thread.current[:"attr_#{name}_#{sym}"] = obj + end + EOS + + unless options[:instance_writer] == false || options[:instance_accessor] == false + class_eval(<<-EOS, __FILE__, __LINE__ + 1) + def #{sym}=(obj) + Thread.current[:"attr_#{self.class.name}_#{sym}"] = obj + end + EOS + end + end + end + alias :thread_cattr_writer :thread_mattr_writer + + # Defines both class and instance accessors for class attributes. + # + # class Account + # thread_mattr_accessor :user + # end + # + # Account.user = "DHH" + # Account.user # => "DHH" + # Account.new.user # => "DHH" + # + # If a subclass changes the value, the parent class' value is not changed. + # Similarly, if the parent class changes the value, the value of subclasses + # is not changed. + # + # class Customer < Account + # end + # + # Customer.user = "Rafael" + # Customer.user # => "Rafael" + # Account.user # => "DHH" + # + # To opt out of the instance writer method, pass <tt>instance_writer: false</tt>. + # To opt out of the instance reader method, pass <tt>instance_reader: false</tt>. + # + # class Current + # thread_mattr_accessor :user, instance_writer: false, instance_reader: false + # end + # + # Current.new.user = "DHH" # => NoMethodError + # Current.new.user # => NoMethodError + # + # Or pass <tt>instance_accessor: false</tt>, to opt out both instance methods. + # + # class Current + # mattr_accessor :user, instance_accessor: false + # end + # + # Current.new.user = "DHH" # => NoMethodError + # Current.new.user # => NoMethodError + def thread_mattr_accessor(*syms, &blk) + thread_mattr_reader(*syms, &blk) + thread_mattr_writer(*syms, &blk) + end + alias :thread_cattr_accessor :thread_mattr_accessor +end diff --git a/activesupport/lib/active_support/core_ext/string/output_safety.rb b/activesupport/lib/active_support/core_ext/string/output_safety.rb index 510fa48189..04ed8e7cd8 100644 --- a/activesupport/lib/active_support/core_ext/string/output_safety.rb +++ b/activesupport/lib/active_support/core_ext/string/output_safety.rb @@ -5,7 +5,6 @@ class ERB module Util HTML_ESCAPE = { '&' => '&', '>' => '>', '<' => '<', '"' => '"', "'" => ''' } JSON_ESCAPE = { '&' => '\u0026', '>' => '\u003e', '<' => '\u003c', "\u2028" => '\u2028', "\u2029" => '\u2029' } - HTML_ESCAPE_REGEXP = /[&"'><]/ HTML_ESCAPE_ONCE_REGEXP = /["><']|&(?!([a-zA-Z]+|(#\d+)|(#[xX][\dA-Fa-f]+));)/ JSON_ESCAPE_REGEXP = /[\u2028\u2029&><]/u @@ -37,7 +36,7 @@ class ERB if s.html_safe? s else - ActiveSupport::Multibyte::Unicode.tidy_bytes(s).gsub(HTML_ESCAPE_REGEXP, HTML_ESCAPE) + CGI.escapeHTML(ActiveSupport::Multibyte::Unicode.tidy_bytes(s)) end end module_function :unwrapped_html_escape @@ -243,8 +242,7 @@ module ActiveSupport #:nodoc: private def html_escape_interpolated_argument(arg) - (!html_safe? || arg.html_safe?) ? arg : - arg.to_s.gsub(ERB::Util::HTML_ESCAPE_REGEXP, ERB::Util::HTML_ESCAPE) + (!html_safe? || arg.html_safe?) ? arg : CGI.escapeHTML(arg.to_s) end end end |