diff options
Diffstat (limited to 'activesupport/lib')
30 files changed, 325 insertions, 317 deletions
diff --git a/activesupport/lib/active_support/cache.rb b/activesupport/lib/active_support/cache.rb index 7d032ca984..26e737e917 100644 --- a/activesupport/lib/active_support/cache.rb +++ b/activesupport/lib/active_support/cache.rb @@ -77,8 +77,7 @@ module ActiveSupport def expand_cache_key(key, namespace = nil) expanded_cache_key = namespace ? "#{namespace}/" : "" - prefix = ENV["RAILS_CACHE_ID"] || ENV["RAILS_APP_VERSION"] - if prefix + if prefix = ENV["RAILS_CACHE_ID"] || ENV["RAILS_APP_VERSION"] expanded_cache_key << "#{prefix}/" end @@ -91,7 +90,7 @@ module ActiveSupport def retrieve_cache_key(key) case when key.respond_to?(:cache_key) then key.cache_key - when key.is_a?(Array) then ['Array', *key.map { |element| retrieve_cache_key(element) }].to_param + when key.is_a?(Array) then key.map { |element| retrieve_cache_key(element) }.to_param else key.to_param end.to_s end diff --git a/activesupport/lib/active_support/callbacks.rb b/activesupport/lib/active_support/callbacks.rb index d4a9b93ff2..1834027e7b 100644 --- a/activesupport/lib/active_support/callbacks.rb +++ b/activesupport/lib/active_support/callbacks.rb @@ -66,8 +66,6 @@ module ActiveSupport # # Calls the before and around callbacks in the order they were set, yields # the block (if given one), and then runs the after callbacks in reverse order. - # Optionally accepts a key, which will be used to compile an optimized callback - # method for each key. See +ClassMethods.define_callbacks+ for more information. # # If the callback chain was halted, returns +false+. Otherwise returns the result # of the block, or +true+ if no block is given. @@ -77,7 +75,8 @@ module ActiveSupport # end # def run_callbacks(kind, key = nil, &block) - self.class.__run_callbacks(key, kind, self, &block) + #TODO: deprecate key argument + self.class.__run_callbacks(kind, self, &block) end private @@ -95,12 +94,18 @@ module ActiveSupport def initialize(chain, filter, kind, options, klass) @chain, @kind, @klass = chain, kind, klass + deprecate_per_key_option(options) normalize_options!(options) @raw_filter, @options = filter, options @filter = _compile_filter(filter) - @compiled_options = _compile_options(options) - @callback_id = next_id + recompile_options! + end + + def deprecate_per_key_option(options) + if options[:per_key] + raise NotImplementedError, ":per_key option is no longer supported. Use generic :if and :unless options instead." + end end def clone(chain, klass) @@ -116,11 +121,6 @@ module ActiveSupport def normalize_options!(options) options[:if] = Array(options[:if]) options[:unless] = Array(options[:unless]) - - options[:per_key] ||= {} - - options[:if] += Array(options[:per_key][:if]) - options[:unless] += Array(options[:per_key][:unless]) end def name @@ -136,21 +136,19 @@ module ActiveSupport end def _update_filter(filter_options, new_options) - filter_options[:if].push(new_options[:unless]) if new_options.key?(:unless) - filter_options[:unless].push(new_options[:if]) if new_options.key?(:if) + filter_options[:if].concat(Array(new_options[:unless])) if new_options.key?(:unless) + filter_options[:unless].concat(Array(new_options[:if])) if new_options.key?(:if) end - def recompile!(_options, _per_key) + def recompile!(_options) + deprecate_per_key_option(_options) _update_filter(self.options, _options) - _update_filter(self.options, _per_key) - @callback_id = next_id - @filter = _compile_filter(@raw_filter) - @compiled_options = _compile_options(@options) + recompile_options! end # Wraps code with filter - def apply(code, key=nil, object=nil) + def apply(code) case @kind when :before <<-RUBY_EVAL @@ -169,7 +167,7 @@ module ActiveSupport when :after <<-RUBY_EVAL #{code} - if #{@compiled_options} + if #{!chain.config[:skip_after_callbacks_if_terminated] || "!halted"} && #{@compiled_options} #{@filter} end RUBY_EVAL @@ -220,7 +218,7 @@ module ActiveSupport # Options support the same options as filters themselves (and support # symbols, string, procs, and objects), so compile a conditional # expression based on the options - def _compile_options(options) + def recompile_options! conditions = ["true"] unless options[:if].empty? @@ -231,7 +229,7 @@ module ActiveSupport conditions << Array(_compile_filter(options[:unless])).map {|f| "!#{f}"} end - conditions.flatten.join(" && ") + @compiled_options = conditions.flatten.join(" && ") end # Filters support: @@ -314,14 +312,14 @@ module ActiveSupport }.merge(config) end - def compile(key=nil, object=nil) + def compile method = [] method << "value = nil" method << "halted = false" callbacks = yielding reverse_each do |callback| - callbacks = callback.apply(callbacks, key, object) + callbacks = callback.apply(callbacks) end method << callbacks @@ -352,14 +350,14 @@ module ActiveSupport module ClassMethods - # This method calls the callback method for the given key. - # If this called first time it creates a new callback method for the key, - # calculating which callbacks can be omitted because of per_key conditions. + # This method runs callback chain for the given kind. + # If this called first time it creates a new callback method for the kind. + # This generated method plays caching role. # - def __run_callbacks(key, kind, object, &blk) #:nodoc: - name = __callback_runner_name(key, kind) + def __run_callbacks(kind, object, &blk) #:nodoc: + name = __callback_runner_name(kind) unless object.respond_to?(name) - str = send("_#{kind}_callbacks").compile(key, object) + str = object.send("_#{kind}_callbacks").compile class_eval <<-RUBY_EVAL, __FILE__, __LINE__ + 1 def #{name}() #{str} end protected :#{name} @@ -369,11 +367,11 @@ module ActiveSupport end def __reset_runner(symbol) - name = __callback_runner_name(nil, symbol) + name = __callback_runner_name(symbol) undef_method(name) if method_defined?(name) end - def __callback_runner_name(key, kind) + def __callback_runner_name(kind) "_run__#{self.name.hash.abs}__#{kind}__callbacks" end @@ -427,29 +425,6 @@ module ActiveSupport # will be called only when it returns a false value. # * <tt>:prepend</tt> - If true, the callback will be prepended to the existing # chain rather than appended. - # * <tt>:per_key</tt> - A hash with <tt>:if</tt> and <tt>:unless</tt> options; - # see "Per-key conditions" below. - # - # ===== Per-key conditions - # - # When creating or skipping callbacks, you can specify conditions that - # are always the same for a given key. For instance, in Action Pack, - # we convert :only and :except conditions into per-key conditions. - # - # before_filter :authenticate, :except => "index" - # - # becomes - # - # set_callback :process_action, :before, :authenticate, :per_key => {:unless => proc {|c| c.action_name == "index"}} - # - # Per-key conditions are evaluated only once per use of a given key. - # In the case of the above example, you would do: - # - # run_callbacks(:process_action, action_name) { ... dispatch stuff ... } - # - # In that case, each action_name would get its own compiled callback - # method that took into consideration the per_key conditions. This - # is a speed improvement for ActionPack. # def set_callback(name, *filter_list, &block) mapped = nil @@ -484,7 +459,7 @@ module ActiveSupport if filter && options.any? new_filter = filter.clone(chain, self) chain.insert(chain.index(filter), new_filter) - new_filter.recompile!(options, options[:per_key] || {}) + new_filter.recompile!(options) end chain.delete(filter) @@ -528,6 +503,11 @@ module ActiveSupport # other callbacks are not executed. Defaults to "false", meaning no value # halts the chain. # + # * <tt>:skip_after_callbacks_if_terminated</tt> - Determines if after callbacks should be terminated + # by the <tt>:terminator</tt> option. By default after callbacks executed no matter + # if callback chain was terminated or not. + # Option makes sence only when <tt>:terminator</tt> option is specified. + # # * <tt>:rescuable</tt> - By default, after filters are not executed if # the given block or a before filter raises an error. By setting this option # to <tt>true</tt> exception raised by given block is stored and after diff --git a/activesupport/lib/active_support/core_ext/date_time/calculations.rb b/activesupport/lib/active_support/core_ext/date_time/calculations.rb index 6dd321ecf9..6f730e4b6f 100644 --- a/activesupport/lib/active_support/core_ext/date_time/calculations.rb +++ b/activesupport/lib/active_support/core_ext/date_time/calculations.rb @@ -1,8 +1,12 @@ +require 'active_support/deprecation' + class DateTime class << self - # DateTimes aren't aware of DST rules, so use a consistent non-DST offset when creating a DateTime with an offset in the local zone + # *DEPRECATED*: Use +DateTime.civil_from_format+ directly. def local_offset - ::Time.local(2007).utc_offset.to_r / 86400 + ActiveSupport::Deprecation.warn 'DateTime.local_offset is deprecated. ' \ + 'Use DateTime.civil_from_format directly.', caller + ::Time.local(2012).utc_offset.to_r / 86400 end # Returns <tt>Time.zone.now.to_datetime</tt> when <tt>Time.zone</tt> or <tt>config.time_zone</tt> are set, otherwise returns <tt>Time.now.to_datetime</tt>. diff --git a/activesupport/lib/active_support/core_ext/date_time/conversions.rb b/activesupport/lib/active_support/core_ext/date_time/conversions.rb index d7b3ad7d8d..dc55e9c33c 100644 --- a/activesupport/lib/active_support/core_ext/date_time/conversions.rb +++ b/activesupport/lib/active_support/core_ext/date_time/conversions.rb @@ -58,32 +58,27 @@ class DateTime alias_method :default_inspect, :inspect alias_method :inspect, :readable_inspect - # Converts self to a Ruby Date object; time portion is discarded. - def to_date - ::Date.new(year, month, day) - end unless instance_methods(false).include?(:to_date) - # Attempts to convert self to a Ruby Time object; returns self if out of range of Ruby Time class. # If self has an offset other than 0, self will just be returned unaltered, since there's no clean way to map it to a Time. def to_time self.offset == 0 ? ::Time.utc_time(year, month, day, hour, min, sec, sec_fraction * 1000000) : self end - # To be able to keep Times, Dates and DateTimes interchangeable on conversions. - def to_datetime - self - end unless instance_methods(false).include?(:to_datetime) - + # Returns DateTime with local offset for given year if format is local else offset is zero + # + # DateTime.civil_from_format :local, 2012 + # # => Sun, 01 Jan 2012 00:00:00 +0300 + # DateTime.civil_from_format :local, 2012, 12, 17 + # # => Mon, 17 Dec 2012 00:00:00 +0000 def self.civil_from_format(utc_or_local, year, month=1, day=1, hour=0, min=0, sec=0) - offset = utc_or_local.to_sym == :local ? local_offset : 0 + if utc_or_local.to_sym == :local + offset = ::Time.local(year, month, day).utc_offset.to_r / 86400 + else + offset = 0 + end civil(year, month, day, hour, min, sec, offset) end - # Converts datetime to an appropriate format for use in XML. - def xmlschema - strftime("%Y-%m-%dT%H:%M:%S%Z") - end unless instance_methods(false).include?(:xmlschema) - # Converts self to a floating-point number of seconds since the Unix epoch. def to_f seconds_since_unix_epoch.to_f diff --git a/activesupport/lib/active_support/core_ext/integer/inflections.rb b/activesupport/lib/active_support/core_ext/integer/inflections.rb index 0e606056c0..1e30687166 100644 --- a/activesupport/lib/active_support/core_ext/integer/inflections.rb +++ b/activesupport/lib/active_support/core_ext/integer/inflections.rb @@ -14,4 +14,18 @@ class Integer def ordinalize ActiveSupport::Inflector.ordinalize(self) end + + # Ordinal returns the suffix used to denote the position + # in an ordered sequence such as 1st, 2nd, 3rd, 4th. + # + # 1.ordinal # => "st" + # 2.ordinal # => "nd" + # 1002.ordinal # => "nd" + # 1003.ordinal # => "rd" + # -11.ordinal # => "th" + # -1001.ordinal # => "st" + # + def ordinal + ActiveSupport::Inflector.ordinal(self) + end end diff --git a/activesupport/lib/active_support/core_ext/module/delegation.rb b/activesupport/lib/active_support/core_ext/module/delegation.rb index 7de824a77f..ac2a63d3a1 100644 --- a/activesupport/lib/active_support/core_ext/module/delegation.rb +++ b/activesupport/lib/active_support/core_ext/module/delegation.rb @@ -81,25 +81,25 @@ class Module # no matter whether +nil+ responds to the delegated method. You can get a # +nil+ instead with the +:allow_nil+ option. # - # class Foo - # attr_accessor :bar - # def initialize(bar = nil) - # @bar = bar - # end - # delegate :zoo, :to => :bar - # end - # - # Foo.new.zoo # raises NoMethodError exception (you called nil.zoo) - # - # class Foo - # attr_accessor :bar - # def initialize(bar = nil) - # @bar = bar - # end - # delegate :zoo, :to => :bar, :allow_nil => true - # end - # - # Foo.new.zoo # returns nil + # class Foo + # attr_accessor :bar + # def initialize(bar = nil) + # @bar = bar + # end + # delegate :zoo, :to => :bar + # end + # + # Foo.new.zoo # raises NoMethodError exception (you called nil.zoo) + # + # class Foo + # attr_accessor :bar + # def initialize(bar = nil) + # @bar = bar + # end + # delegate :zoo, :to => :bar, :allow_nil => true + # end + # + # Foo.new.zoo # returns nil # def delegate(*methods) options = methods.pop diff --git a/activesupport/lib/active_support/core_ext/range/overlaps.rb b/activesupport/lib/active_support/core_ext/range/overlaps.rb index 7df653b53f..603657c180 100644 --- a/activesupport/lib/active_support/core_ext/range/overlaps.rb +++ b/activesupport/lib/active_support/core_ext/range/overlaps.rb @@ -3,6 +3,6 @@ class Range # (1..5).overlaps?(4..6) # => true # (1..5).overlaps?(7..9) # => false def overlaps?(other) - include?(other.first) || other.include?(first) + cover?(other.first) || other.cover?(first) end end diff --git a/activesupport/lib/active_support/core_ext/string/inflections.rb b/activesupport/lib/active_support/core_ext/string/inflections.rb index 1e57b586d9..2194dafe4d 100644 --- a/activesupport/lib/active_support/core_ext/string/inflections.rb +++ b/activesupport/lib/active_support/core_ext/string/inflections.rb @@ -100,8 +100,8 @@ class String # # +underscore+ will also change '::' to '/' to convert namespaces to paths. # - # "ActiveRecord".underscore # => "active_record" - # "ActiveRecord::Errors".underscore # => active_record/errors + # "ActiveModel".underscore # => "active_model" + # "ActiveModel::Errors".underscore # => "active_model/errors" def underscore ActiveSupport::Inflector.underscore(self) 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 73aa7dd89a..dd780da157 100644 --- a/activesupport/lib/active_support/core_ext/string/output_safety.rb +++ b/activesupport/lib/active_support/core_ext/string/output_safety.rb @@ -5,6 +5,8 @@ class ERB module Util HTML_ESCAPE = { '&' => '&', '>' => '>', '<' => '<', '"' => '"' } JSON_ESCAPE = { '&' => '\u0026', '>' => '\u003E', '<' => '\u003C' } + HTML_ESCAPE_ONCE_REGEXP = /[\"><]|&(?!([a-zA-Z]+|(#\d+));)/ + JSON_ESCAPE_REGEXP = /[&"><]/ # A utility method for escaping HTML tag characters. # This method is also aliased as <tt>h</tt>. @@ -33,6 +35,21 @@ class ERB singleton_class.send(:remove_method, :html_escape) module_function :html_escape + # A utility method for escaping HTML without affecting existing escaped entities. + # + # ==== Examples + # html_escape_once("1 < 2 & 3") + # # => "1 < 2 & 3" + # + # html_escape_once("<< Accept & Checkout") + # # => "<< Accept & Checkout" + def html_escape_once(s) + result = s.to_s.gsub(HTML_ESCAPE_ONCE_REGEXP) { |special| HTML_ESCAPE[special] } + s.html_safe? ? result.html_safe : result + end + + module_function :html_escape_once + # A utility method for escaping HTML entities in JSON strings # using \uXXXX JavaScript escape sequences for string literals: # @@ -51,7 +68,7 @@ class ERB # <%=j @person.to_json %> # def json_escape(s) - result = s.to_s.gsub(/[&"><]/) { |special| JSON_ESCAPE[special] } + result = s.to_s.gsub(JSON_ESCAPE_REGEXP) { |special| JSON_ESCAPE[special] } s.html_safe? ? result.html_safe : result end diff --git a/activesupport/lib/active_support/core_ext/time/calculations.rb b/activesupport/lib/active_support/core_ext/time/calculations.rb index 5e433f5dd9..4f300329f5 100644 --- a/activesupport/lib/active_support/core_ext/time/calculations.rb +++ b/activesupport/lib/active_support/core_ext/time/calculations.rb @@ -273,9 +273,9 @@ class Time beginning_of_day..end_of_day end - # Returns a Range representing the whole week of the current time. - def all_week - beginning_of_week..end_of_week + # Returns a Range representing the whole week of the current time. Week starts on start_day (default is :monday, i.e. end of Sunday). + def all_week(start_day = :monday) + beginning_of_week(start_day)..end_of_week(start_day) end # Returns a Range representing the whole month of the current time. diff --git a/activesupport/lib/active_support/core_ext/uri.rb b/activesupport/lib/active_support/core_ext/uri.rb index 0b219ce44a..bfe0832b37 100644 --- a/activesupport/lib/active_support/core_ext/uri.rb +++ b/activesupport/lib/active_support/core_ext/uri.rb @@ -20,7 +20,7 @@ end module URI class << self def parser - @parser ||= URI.const_defined?(:Parser) ? URI::Parser.new : URI + @parser ||= URI::Parser.new end end end diff --git a/activesupport/lib/active_support/inflections.rb b/activesupport/lib/active_support/inflections.rb index 527cce2594..b3eb1333ca 100644 --- a/activesupport/lib/active_support/inflections.rb +++ b/activesupport/lib/active_support/inflections.rb @@ -2,7 +2,7 @@ module ActiveSupport Inflector.inflections do |inflect| inflect.plural(/$/, 's') inflect.plural(/s$/i, 's') - inflect.plural(/(ax|test)is$/i, '\1es') + inflect.plural(/^(ax|test)is$/i, '\1es') inflect.plural(/(octop|vir)us$/i, '\1i') inflect.plural(/(octop|vir)i$/i, '\1i') inflect.plural(/(alias|status)$/i, '\1es') @@ -23,10 +23,11 @@ module ActiveSupport inflect.plural(/(quiz)$/i, '\1zes') inflect.singular(/s$/i, '') + inflect.singular(/(ss)$/i, '\1') inflect.singular(/(n)ews$/i, '\1ews') inflect.singular(/([ti])a$/i, '\1um') - inflect.singular(/((a)naly|(b)a|(d)iagno|(p)arenthe|(p)rogno|(s)ynop|(t)he)ses$/i, '\1\2sis') - inflect.singular(/(^analy)ses$/i, '\1sis') + inflect.singular(/((a)naly|(b)a|(d)iagno|(p)arenthe|(p)rogno|(s)ynop|(t)he)(sis|ses)$/i, '\1\2sis') + inflect.singular(/(^analy)(sis|ses)$/i, '\1sis') inflect.singular(/([^f])ves$/i, '\1fe') inflect.singular(/(hive)s$/i, '\1') inflect.singular(/(tive)s$/i, '\1') @@ -36,12 +37,13 @@ module ActiveSupport inflect.singular(/(m)ovies$/i, '\1ovie') inflect.singular(/(x|ch|ss|sh)es$/i, '\1') inflect.singular(/(m|l)ice$/i, '\1ouse') - inflect.singular(/(bus)es$/i, '\1') + inflect.singular(/(bus)(es)?$/i, '\1') inflect.singular(/(o)es$/i, '\1') inflect.singular(/(shoe)s$/i, '\1') - inflect.singular(/(cris|ax|test)es$/i, '\1is') - inflect.singular(/(octop|vir)i$/i, '\1us') - inflect.singular(/(alias|status)es$/i, '\1') + inflect.singular(/(cris|test)(is|es)$/i, '\1is') + inflect.singular(/^(a)x[ie]s$/i, '\1xis') + inflect.singular(/(octop|vir)(us|i)$/i, '\1us') + inflect.singular(/(alias|status)(es)?$/i, '\1') inflect.singular(/^(ox)en/i, '\1') inflect.singular(/(vert|ind)ices$/i, '\1ex') inflect.singular(/(matr)ices$/i, '\1ix') diff --git a/activesupport/lib/active_support/inflector/inflections.rb b/activesupport/lib/active_support/inflector/inflections.rb index 90bb62f57b..8cd96fe2d1 100644 --- a/activesupport/lib/active_support/inflector/inflections.rb +++ b/activesupport/lib/active_support/inflector/inflections.rb @@ -1,3 +1,5 @@ +require 'active_support/core_ext/array/prepend_and_append' + module ActiveSupport module Inflector # A singleton instance of this class is yielded by Inflector.inflections, which can then be used to specify additional @@ -82,7 +84,7 @@ module ActiveSupport def plural(rule, replacement) @uncountables.delete(rule) if rule.is_a?(String) @uncountables.delete(replacement) - @plurals.insert(0, [rule, replacement]) + @plurals.prepend([rule, replacement]) end # Specifies a new singularization rule and its replacement. The rule can either be a string or a regular expression. @@ -90,7 +92,7 @@ module ActiveSupport def singular(rule, replacement) @uncountables.delete(rule) if rule.is_a?(String) @uncountables.delete(replacement) - @singulars.insert(0, [rule, replacement]) + @singulars.prepend([rule, replacement]) end # Specifies a new irregular that applies to both pluralization and singularization at the same time. This can only be used @@ -134,7 +136,7 @@ module ActiveSupport # human /_cnt$/i, '\1_count' # human "legacy_col_person_name", "Name" def human(rule, replacement) - @humans.insert(0, [rule, replacement]) + @humans.prepend([rule, replacement]) end # Clears the loaded inflections within a given scope (default is <tt>:all</tt>). diff --git a/activesupport/lib/active_support/inflector/methods.rb b/activesupport/lib/active_support/inflector/methods.rb index 7f325aee94..c630bd21b1 100644 --- a/activesupport/lib/active_support/inflector/methods.rb +++ b/activesupport/lib/active_support/inflector/methods.rb @@ -42,10 +42,10 @@ module ActiveSupport # +camelize+ will also convert '/' to '::' which is useful for converting paths to namespaces. # # Examples: - # "active_record".camelize # => "ActiveRecord" - # "active_record".camelize(:lower) # => "activeRecord" - # "active_record/errors".camelize # => "ActiveRecord::Errors" - # "active_record/errors".camelize(:lower) # => "activeRecord::Errors" + # "active_model".camelize # => "ActiveModel" + # "active_model".camelize(:lower) # => "activeModel" + # "active_model/errors".camelize # => "ActiveModel::Errors" + # "active_model/errors".camelize(:lower) # => "activeModel::Errors" # # As a rule of thumb you can think of +camelize+ as the inverse of +underscore+, # though there are cases where that does not hold: @@ -66,8 +66,8 @@ module ActiveSupport # Changes '::' to '/' to convert namespaces to paths. # # Examples: - # "ActiveRecord".underscore # => "active_record" - # "ActiveRecord::Errors".underscore # => active_record/errors + # "ActiveModel".underscore # => "active_model" + # "ActiveModel::Errors".underscore # => "active_model/errors" # # As a rule of thumb you can think of +underscore+ as the inverse of +camelize+, # though there are cases where that does not hold: @@ -92,9 +92,12 @@ module ActiveSupport # "author_id" # => "Author" def humanize(lower_case_and_underscored_word) result = lower_case_and_underscored_word.to_s.dup - inflections.humans.each { |(rule, replacement)| break if result.gsub!(rule, replacement) } + inflections.humans.each { |(rule, replacement)| break if result.sub!(rule, replacement) } result.gsub!(/_id$/, "") - result.gsub(/(_)?([a-z\d]*)/i) { "#{$1 && ' '}#{inflections.acronyms[$2] || $2.downcase}" }.gsub(/^\w/) { $&.upcase } + result.tr!('_', ' ') + result.gsub(/([a-z\d]*)/i) { |match| + "#{inflections.acronyms[match] || match.downcase}" + }.gsub(/^\w/) { $&.upcase } end # Capitalizes all the words and replaces some characters in the string to create @@ -143,7 +146,7 @@ module ActiveSupport # Example: # "puni_puni" # => "puni-puni" def dasherize(underscored_word) - underscored_word.gsub(/_/, '-') + underscored_word.tr('_', '-') end # Removes the module part from the expression in the string: @@ -209,7 +212,7 @@ module ActiveSupport constant = Object names.each do |name| - constant = constant.const_defined?(name, false) ? constant.const_get(name) : constant.const_missing(name) + constant = constant.const_get(name, false) end constant end @@ -240,13 +243,36 @@ module ActiveSupport begin constantize(camel_cased_word) rescue NameError => e - raise unless e.message =~ /uninitialized constant #{const_regexp(camel_cased_word)}$/ || + raise unless e.message =~ /(uninitialized constant|wrong constant name) #{const_regexp(camel_cased_word)}$/ || e.name.to_s == camel_cased_word.to_s rescue ArgumentError => e raise unless e.message =~ /not missing constant #{const_regexp(camel_cased_word)}\!$/ end end + # Returns the suffix that should be added to a number to denote the position + # in an ordered sequence such as 1st, 2nd, 3rd, 4th. + # + # Examples: + # ordinal(1) # => "st" + # ordinal(2) # => "nd" + # ordinal(1002) # => "nd" + # ordinal(1003) # => "rd" + # ordinal(-11) # => "th" + # ordinal(-1021) # => "st" + def ordinal(number) + if (11..13).include?(number.to_i.abs % 100) + "th" + else + case number.to_i.abs % 10 + when 1; "st" + when 2; "nd" + when 3; "rd" + else "th" + end + end + end + # Turns a number into an ordinal string used to denote the position in an # ordered sequence such as 1st, 2nd, 3rd, 4th. # @@ -258,16 +284,7 @@ module ActiveSupport # ordinalize(-11) # => "-11th" # ordinalize(-1021) # => "-1021st" def ordinalize(number) - if (11..13).include?(number.to_i.abs % 100) - "#{number}th" - else - case number.to_i.abs % 10 - when 1; "#{number}st" - when 2; "#{number}nd" - when 3; "#{number}rd" - else "#{number}th" - end - end + "#{number}#{ordinal(number)}" end private @@ -291,10 +308,10 @@ module ActiveSupport def apply_inflections(word, rules) result = word.to_s.dup - if word.empty? || inflections.uncountables.any? { |inflection| result =~ /\b#{inflection}\Z/i } + if word.empty? || inflections.uncountables.include?(result.downcase[/\b\w+\Z/]) result else - rules.each { |(rule, replacement)| break if result.gsub!(rule, replacement) } + rules.each { |(rule, replacement)| break if result.sub!(rule, replacement) } result end end diff --git a/activesupport/lib/active_support/json/encoding.rb b/activesupport/lib/active_support/json/encoding.rb index fcd83f8dea..b2adfea273 100644 --- a/activesupport/lib/active_support/json/encoding.rb +++ b/activesupport/lib/active_support/json/encoding.rb @@ -1,7 +1,6 @@ require 'active_support/core_ext/object/to_json' require 'active_support/core_ext/module/delegation' require 'active_support/json/variable' -require 'active_support/ordered_hash' require 'bigdecimal' require 'active_support/core_ext/big_decimal/conversions' # for #to_s @@ -239,8 +238,7 @@ class Hash # use encoder as a proxy to call as_json on all values in the subset, to protect from circular references encoder = options && options[:encoder] || ActiveSupport::JSON::Encoding::Encoder.new(options) - result = self.is_a?(ActiveSupport::OrderedHash) ? ActiveSupport::OrderedHash : Hash - result[subset.map { |k, v| [k.to_s, encoder.as_json(v, options)] }] + Hash[subset.map { |k, v| [k.to_s, encoder.as_json(v, options)] }] end def encode_json(encoder) diff --git a/activesupport/lib/active_support/log_subscriber.rb b/activesupport/lib/active_support/log_subscriber.rb index 6296c1d4b8..58938cdc3d 100644 --- a/activesupport/lib/active_support/log_subscriber.rb +++ b/activesupport/lib/active_support/log_subscriber.rb @@ -92,7 +92,7 @@ module ActiveSupport begin send(method, ActiveSupport::Notifications::Event.new(message, *args)) rescue Exception => e - logger.error "Could not log #{message.inspect} event. #{e.class}: #{e.message}" + logger.error "Could not log #{message.inspect} event. #{e.class}: #{e.message} #{e.backtrace}" end end @@ -100,9 +100,9 @@ module ActiveSupport %w(info debug warn error fatal unknown).each do |level| class_eval <<-METHOD, __FILE__, __LINE__ + 1 - def #{level}(*args, &block) + def #{level}(progname = nil, &block) return unless logger - logger.#{level}(*args, &block) + logger.#{level}(progname, &block) end METHOD end diff --git a/activesupport/lib/active_support/logger.rb b/activesupport/lib/active_support/logger.rb index 66e8fcadb4..d055767eab 100644 --- a/activesupport/lib/active_support/logger.rb +++ b/activesupport/lib/active_support/logger.rb @@ -2,6 +2,41 @@ require 'logger' module ActiveSupport class Logger < ::Logger + # Broadcasts logs to multiple loggers + def self.broadcast(logger) # :nodoc: + Module.new do + define_method(:add) do |*args, &block| + logger.add(*args, &block) + super(*args, &block) + end + + define_method(:<<) do |x| + logger << x + super(x) + end + + define_method(:close) do + logger.close + super() + end + + define_method(:progname=) do |name| + logger.progname = name + super(name) + end + + define_method(:formatter=) do |formatter| + logger.formatter = formatter + super(formatter) + end + + define_method(:level=) do |level| + logger.level = level + super(level) + end + end + end + def initialize(*args) super @formatter = SimpleFormatter.new diff --git a/activesupport/lib/active_support/message_encryptor.rb b/activesupport/lib/active_support/message_encryptor.rb index 6ec5a04933..ada2e79ccb 100644 --- a/activesupport/lib/active_support/message_encryptor.rb +++ b/activesupport/lib/active_support/message_encryptor.rb @@ -9,6 +9,11 @@ module ActiveSupport # # This can be used in situations similar to the <tt>MessageVerifier</tt>, but where you don't # want users to be able to determine the value of the payload. + # + # key = OpenSSL::Digest::SHA256.new('password').digest # => "\x89\xE0\x156\xAC..." + # crypt = ActiveSupport::MessageEncryptor.new(key) # => #<ActiveSupport::MessageEncryptor ...> + # encrypted_data = crypt.encrypt_and_sign('my secret data') # => "NlFBTTMwOUV5UlA1QlNEN2xkY2d6eThYWWh..." + # crypt.decrypt_and_verify(encrypted_data) # => "my secret data" class MessageEncryptor module NullSerializer #:nodoc: def self.load(value) @@ -23,6 +28,15 @@ module ActiveSupport class InvalidMessage < StandardError; end OpenSSLCipherError = OpenSSL::Cipher.const_defined?(:CipherError) ? OpenSSL::Cipher::CipherError : OpenSSL::CipherError + # Initialize a new MessageEncryptor. + # +secret+ must be at least as long as the cipher key size. For the default 'aes-256-cbc' cipher, + # this is 256 bits. If you are using a user-entered secret, you can generate a suitable key with + # <tt>OpenSSL::Digest::SHA256.new(user_secret).digest</tt> or similar. + # + # Options: + # * <tt>:cipher</tt> - Cipher to use. Can be any cipher returned by <tt>OpenSSL::Cipher.ciphers</tt>. Default is 'aes-256-cbc' + # * <tt>:serializer</tt> - Object serializer to use. Default is +Marshal+. + # def initialize(secret, options = {}) @secret = secret @cipher = options[:cipher] || 'aes-256-cbc' diff --git a/activesupport/lib/active_support/multibyte/chars.rb b/activesupport/lib/active_support/multibyte/chars.rb index ac61870871..9a748dfa60 100644 --- a/activesupport/lib/active_support/multibyte/chars.rb +++ b/activesupport/lib/active_support/multibyte/chars.rb @@ -1,4 +1,5 @@ # encoding: utf-8 +require 'active_support/json' require 'active_support/core_ext/string/access' require 'active_support/core_ext/string/behavior' require 'active_support/core_ext/module/delegation' @@ -188,6 +189,10 @@ module ActiveSupport #:nodoc: chars(Unicode.tidy_bytes(@wrapped_string, force)) end + def as_json(options = nil) #:nodoc: + to_s.as_json(options) + end + %w(capitalize downcase reverse tidy_bytes upcase).each do |method| define_method("#{method}!") do |*args| @wrapped_string = send(method, *args).to_s diff --git a/activesupport/lib/active_support/multibyte/unicode.rb b/activesupport/lib/active_support/multibyte/unicode.rb index a0a8f3c97e..cb89d45c92 100644 --- a/activesupport/lib/active_support/multibyte/unicode.rb +++ b/activesupport/lib/active_support/multibyte/unicode.rb @@ -10,7 +10,7 @@ module ActiveSupport NORMALIZATION_FORMS = [:c, :kc, :d, :kd] # The Unicode version that is supported by the implementation - UNICODE_VERSION = '6.0.0' + UNICODE_VERSION = '6.1.0' # The default normalization used for operations that require normalization. It can be set to any of the # normalizations in NORMALIZATION_FORMS. diff --git a/activesupport/lib/active_support/ordered_options.rb b/activesupport/lib/active_support/ordered_options.rb index bf81567d22..9e5a5d0246 100644 --- a/activesupport/lib/active_support/ordered_options.rb +++ b/activesupport/lib/active_support/ordered_options.rb @@ -30,8 +30,9 @@ module ActiveSupport #:nodoc: end def method_missing(name, *args) - if name.to_s =~ /(.*)=$/ - self[$1] = args.first + name_string = name.to_s + if name_string.chomp!('=') + self[name_string] = args.first else self[name] end diff --git a/activesupport/lib/active_support/tagged_logging.rb b/activesupport/lib/active_support/tagged_logging.rb index b60bc94db4..f6ad861353 100644 --- a/activesupport/lib/active_support/tagged_logging.rb +++ b/activesupport/lib/active_support/tagged_logging.rb @@ -1,62 +1,58 @@ require 'active_support/core_ext/object/blank' require 'logger' +require 'active_support/logger' module ActiveSupport - # Wraps any standard Logger class to provide tagging capabilities. Examples: + # Wraps any standard Logger object to provide tagging capabilities. Examples: # - # Logger = ActiveSupport::TaggedLogging.new(Logger.new(STDOUT)) - # Logger.tagged("BCX") { Logger.info "Stuff" } # Logs "[BCX] Stuff" - # Logger.tagged("BCX", "Jason") { Logger.info "Stuff" } # Logs "[BCX] [Jason] Stuff" - # Logger.tagged("BCX") { Logger.tagged("Jason") { Logger.info "Stuff" } } # Logs "[BCX] [Jason] Stuff" + # logger = ActiveSupport::TaggedLogging.new(Logger.new(STDOUT)) + # logger.tagged("BCX") { logger.info "Stuff" } # Logs "[BCX] Stuff" + # logger.tagged("BCX", "Jason") { logger.info "Stuff" } # Logs "[BCX] [Jason] Stuff" + # logger.tagged("BCX") { logger.tagged("Jason") { logger.info "Stuff" } } # Logs "[BCX] [Jason] Stuff" # # This is used by the default Rails.logger as configured by Railties to make it easy to stamp log lines # with subdomains, request ids, and anything else to aid debugging of multi-user production applications. - class TaggedLogging - def initialize(logger) - @logger = logger + module TaggedLogging + class Formatter < ActiveSupport::Logger::SimpleFormatter # :nodoc: + # This method is invoked when a log event occurs + def call(severity, timestamp, progname, msg) + super(severity, timestamp, progname, "#{tags_text}#{msg}") + end + + def clear! + current_tags.clear + end + + def current_tags + Thread.current[:activesupport_tagged_logging_tags] ||= [] + end + + private + def tags_text + tags = current_tags + if tags.any? + tags.collect { |tag| "[#{tag}] " }.join + end + end + end + + def self.new(logger) + logger.formatter = Formatter.new + logger.extend(self) end def tagged(*new_tags) - tags = current_tags - new_tags = Array(new_tags).flatten.reject(&:blank?) + tags = formatter.current_tags + new_tags = new_tags.flatten.reject(&:blank?) tags.concat new_tags yield ensure tags.pop(new_tags.size) end - def add(severity, message = nil, progname = nil, &block) - @logger.add(severity, "#{tags_text}#{message}", progname, &block) - end - - %w( fatal error warn info debug unknown ).each do |severity| - eval <<-EOM, nil, __FILE__, __LINE__ + 1 - def #{severity}(progname = nil, &block) # def warn(progname = nil, &block) - add(Logger::#{severity.upcase}, progname, &block) # add(Logger::WARN, progname, &block) - end # end - EOM - end - def flush - current_tags.clear - @logger.flush if @logger.respond_to?(:flush) - end - - def method_missing(method, *args) - @logger.send(method, *args) - end - - protected - - def tags_text - tags = current_tags - if tags.any? - tags.collect { |tag| "[#{tag}] " }.join - end - end - - def current_tags - Thread.current[:activesupport_tagged_logging_tags] ||= [] + formatter.clear! + super if defined?(super) end end end diff --git a/activesupport/lib/active_support/test_case.rb b/activesupport/lib/active_support/test_case.rb index 4169557286..9a52c916ec 100644 --- a/activesupport/lib/active_support/test_case.rb +++ b/activesupport/lib/active_support/test_case.rb @@ -3,7 +3,6 @@ require 'active_support/testing/setup_and_teardown' require 'active_support/testing/assertions' require 'active_support/testing/deprecation' require 'active_support/testing/declarative' -require 'active_support/testing/pending' require 'active_support/testing/isolation' require 'active_support/testing/mochaing' require 'active_support/core_ext/kernel/reporting' @@ -40,7 +39,6 @@ module ActiveSupport include ActiveSupport::Testing::SetupAndTeardown include ActiveSupport::Testing::Assertions include ActiveSupport::Testing::Deprecation - include ActiveSupport::Testing::Pending extend ActiveSupport::Testing::Declarative # test/unit backwards compatibility methods diff --git a/activesupport/lib/active_support/testing/isolation.rb b/activesupport/lib/active_support/testing/isolation.rb index c896b955fb..1a0681e850 100644 --- a/activesupport/lib/active_support/testing/isolation.rb +++ b/activesupport/lib/active_support/testing/isolation.rb @@ -37,10 +37,6 @@ module ActiveSupport !ENV["NO_FORK"] && ((RbConfig::CONFIG['host_os'] !~ /mswin|mingw/) && (RUBY_PLATFORM !~ /java/)) end - def self.included(base) - base.send :include, MiniTest - end - def _run_class_setup # class setup method should only happen in parent unless defined?(@@ran_class_setup) || ENV['ISOLATION_TEST'] self.class.setup if self.class.respond_to?(:setup) @@ -48,18 +44,16 @@ module ActiveSupport end end - module MiniTest - def run(runner) - _run_class_setup + def run(runner) + _run_class_setup - serialized = run_in_isolation do |isolated_runner| - super(isolated_runner) - end - - retval, proxy = Marshal.load(serialized) - proxy.__replay__(runner) - retval + serialized = run_in_isolation do |isolated_runner| + super(isolated_runner) end + + retval, proxy = Marshal.load(serialized) + proxy.__replay__(runner) + retval end module Forking @@ -117,13 +111,3 @@ module ActiveSupport end end end - -# Only in subprocess for windows / jruby. -if ENV['ISOLATION_TEST'] - require "test/unit/collector/objectspace" - class Test::Unit::Collector::ObjectSpace - def include?(test) - super && test.method_name == ENV['ISOLATION_TEST'] - end - end -end diff --git a/activesupport/lib/active_support/testing/pending.rb b/activesupport/lib/active_support/testing/pending.rb deleted file mode 100644 index 510f80f32c..0000000000 --- a/activesupport/lib/active_support/testing/pending.rb +++ /dev/null @@ -1,20 +0,0 @@ -# Some code from jeremymcanally's "pending" -# https://github.com/jeremymcanally/pending/tree/master - -module ActiveSupport - module Testing - module Pending - - unless defined?(Spec) - - @@pending_cases = [] - @@at_exit = false - - def pending(description = "", &block) - skip(description.blank? ? nil : description) - end - end - - end - end -end diff --git a/activesupport/lib/active_support/testing/performance.rb b/activesupport/lib/active_support/testing/performance.rb index 209bfac19f..244ee1a224 100644 --- a/activesupport/lib/active_support/testing/performance.rb +++ b/activesupport/lib/active_support/testing/performance.rb @@ -13,7 +13,6 @@ module ActiveSupport included do superclass_delegating_accessor :profile_options self.profile_options = {} - include ForMiniTest end # each implementation should define metrics and freeze the defaults @@ -36,40 +35,38 @@ module ActiveSupport "#{self.class.name}##{method_name}" end - module ForMiniTest - def run(runner) - @runner = runner + def run(runner) + @runner = runner - run_warmup - if full_profile_options && metrics = full_profile_options[:metrics] - metrics.each do |metric_name| - if klass = Metrics[metric_name.to_sym] - run_profile(klass.new) - end + run_warmup + if full_profile_options && metrics = full_profile_options[:metrics] + metrics.each do |metric_name| + if klass = Metrics[metric_name.to_sym] + run_profile(klass.new) end end - - return end - def run_test(metric, mode) - result = '.' + return + end + + def run_test(metric, mode) + result = '.' + begin + run_callbacks :setup + setup + metric.send(mode) { __send__ method_name } + rescue Exception => e + result = @runner.puke(self.class, method_name, e) + ensure begin - run_callbacks :setup - setup - metric.send(mode) { __send__ method_name } + teardown + run_callbacks :teardown, :enumerator => :reverse_each rescue Exception => e result = @runner.puke(self.class, method_name, e) - ensure - begin - teardown - run_callbacks :teardown, :enumerator => :reverse_each - rescue Exception => e - result = @runner.puke(self.class, method_name, e) - end end - result end + result end protected diff --git a/activesupport/lib/active_support/testing/performance/ruby.rb b/activesupport/lib/active_support/testing/performance/ruby.rb index 26731c6bd7..b7a34ea279 100644 --- a/activesupport/lib/active_support/testing/performance/ruby.rb +++ b/activesupport/lib/active_support/testing/performance/ruby.rb @@ -86,9 +86,12 @@ module ActiveSupport end protected - # overridden by each implementation def with_gc_stats + GC::Profiler.enable + GC.start yield + ensure + GC::Profiler.disable end end @@ -124,27 +127,42 @@ module ActiveSupport class Memory < DigitalInformationUnit Mode = RubyProf::MEMORY if RubyProf.const_defined?(:MEMORY) + + # Ruby 1.9 + GCdata patch + if GC.respond_to?(:malloc_allocated_size) + def measure + GC.malloc_allocated_size + end + end end class Objects < Amount Mode = RubyProf::ALLOCATIONS if RubyProf.const_defined?(:ALLOCATIONS) + + # Ruby 1.9 + GCdata patch + if GC.respond_to?(:malloc_allocations) + def measure + GC.malloc_allocations + end + end end class GcRuns < Amount Mode = RubyProf::GC_RUNS if RubyProf.const_defined?(:GC_RUNS) + + def measure + GC.count + end end class GcTime < Time Mode = RubyProf::GC_TIME if RubyProf.const_defined?(:GC_TIME) + + def measure + GC::Profiler.total_time + end end end end end end - -if RUBY_VERSION.between?('1.9.2', '2.0') - require 'active_support/testing/performance/ruby/yarv' -else - $stderr.puts 'Update your ruby interpreter to be able to run benchmarks.' - exit -end diff --git a/activesupport/lib/active_support/testing/performance/ruby/yarv.rb b/activesupport/lib/active_support/testing/performance/ruby/yarv.rb deleted file mode 100644 index c34d31bf10..0000000000 --- a/activesupport/lib/active_support/testing/performance/ruby/yarv.rb +++ /dev/null @@ -1,48 +0,0 @@ -module ActiveSupport - module Testing - module Performance - module Metrics - class Base - protected - def with_gc_stats - GC::Profiler.enable - GC.start - yield - ensure - GC::Profiler.disable - end - end - - class Memory < DigitalInformationUnit - # Ruby 1.9 + GCdata patch - if GC.respond_to?(:malloc_allocated_size) - def measure - GC.malloc_allocated_size - end - end - end - - class Objects < Amount - # Ruby 1.9 + GCdata patch - if GC.respond_to?(:malloc_allocations) - def measure - GC.malloc_allocations - end - end - end - - class GcRuns < Amount - def measure - GC.count - end - end - - class GcTime < Time - def measure - GC::Profiler.total_time - end - end - end - end - end -end diff --git a/activesupport/lib/active_support/values/unicode_tables.dat b/activesupport/lib/active_support/values/unicode_tables.dat Binary files differindex 7edc4663e8..df17a8cccf 100644 --- a/activesupport/lib/active_support/values/unicode_tables.dat +++ b/activesupport/lib/active_support/values/unicode_tables.dat diff --git a/activesupport/lib/active_support/xml_mini/jdom.rb b/activesupport/lib/active_support/xml_mini/jdom.rb index 6c222b83ba..dbb6c71907 100644 --- a/activesupport/lib/active_support/xml_mini/jdom.rb +++ b/activesupport/lib/active_support/xml_mini/jdom.rb @@ -71,7 +71,7 @@ module ActiveSupport child_nodes = element.child_nodes if child_nodes.length > 0 - for i in 0...child_nodes.length + (0...child_nodes.length).each do |i| child = child_nodes.item(i) merge_element!(hash, child) unless child.node_type == Node.TEXT_NODE end @@ -133,7 +133,7 @@ module ActiveSupport def get_attributes(element) attribute_hash = {} attributes = element.attributes - for i in 0...attributes.length + (0...attributes.length).each do |i| attribute_hash[CONTENT_KEY] ||= '' attribute_hash[attributes.item(i).name] = attributes.item(i).value end @@ -147,7 +147,7 @@ module ActiveSupport def texts(element) texts = [] child_nodes = element.child_nodes - for i in 0...child_nodes.length + (0...child_nodes.length).each do |i| item = child_nodes.item(i) if item.node_type == Node.TEXT_NODE texts << item.get_data @@ -163,7 +163,7 @@ module ActiveSupport def empty_content?(element) text = '' child_nodes = element.child_nodes - for i in 0...child_nodes.length + (0...child_nodes.length).each do |i| item = child_nodes.item(i) if item.node_type == Node.TEXT_NODE text << item.get_data.strip |