diff options
Diffstat (limited to 'activesupport/lib/active_support')
110 files changed, 1303 insertions, 1079 deletions
diff --git a/activesupport/lib/active_support/backtrace_cleaner.rb b/activesupport/lib/active_support/backtrace_cleaner.rb index 8f8deb9692..e97bb25b9f 100644 --- a/activesupport/lib/active_support/backtrace_cleaner.rb +++ b/activesupport/lib/active_support/backtrace_cleaner.rb @@ -1,11 +1,11 @@ module ActiveSupport - # Backtraces often include many lines that are not relevant for the context under review. This makes it hard to find the + # Backtraces often include many lines that are not relevant for the context under review. This makes it hard to find the # signal amongst the backtrace noise, and adds debugging time. With a BacktraceCleaner, filters and silencers are used to # remove the noisy lines, so that only the most relevant lines remain. # # Filters are used to modify lines of data, while silencers are used to remove lines entirely. The typical filter use case - # is to remove lengthy path information from the start of each line, and view file paths relevant to the app directory - # instead of the file system root. The typical silencer use case is to exclude the output of a noisy library from the + # is to remove lengthy path information from the start of each line, and view file paths relevant to the app directory + # instead of the file system root. The typical silencer use case is to exclude the output of a noisy library from the # backtrace, so that you can focus on the rest. # # ==== Example: @@ -15,9 +15,9 @@ module ActiveSupport # bc.add_silencer { |line| line =~ /mongrel|rubygems/ } # bc.clean(exception.backtrace) # will strip the Rails.root prefix and skip any lines from mongrel or rubygems # - # To reconfigure an existing BacktraceCleaner (like the default one in Rails) and show as much data as possible, you can - # always call <tt>BacktraceCleaner#remove_silencers!</tt>, which will restore the backtrace to a pristine state. If you - # need to reconfigure an existing BacktraceCleaner so that it does not filter or modify the paths of any lines of the + # To reconfigure an existing BacktraceCleaner (like the default one in Rails) and show as much data as possible, you can + # always call <tt>BacktraceCleaner#remove_silencers!</tt>, which will restore the backtrace to a pristine state. If you + # need to reconfigure an existing BacktraceCleaner so that it does not filter or modify the paths of any lines of the # backtrace, you can call BacktraceCleaner#remove_filters! These two methods will give you a completely untouched backtrace. # # Inspired by the Quiet Backtrace gem by Thoughtbot. @@ -50,7 +50,7 @@ module ActiveSupport @filters << block end - # Adds a silencer from the block provided. If the silencer returns true for a given line, it will be excluded from + # Adds a silencer from the block provided. If the silencer returns true for a given line, it will be excluded from # the clean backtrace. # # Example: diff --git a/activesupport/lib/active_support/basic_object.rb b/activesupport/lib/active_support/basic_object.rb index c3c7ab0112..6ccb0cd525 100644 --- a/activesupport/lib/active_support/basic_object.rb +++ b/activesupport/lib/active_support/basic_object.rb @@ -10,5 +10,4 @@ module ActiveSupport ::Object.send(:raise, *args) end end - end diff --git a/activesupport/lib/active_support/benchmarkable.rb b/activesupport/lib/active_support/benchmarkable.rb index cc94041a1d..f149a7f0ed 100644 --- a/activesupport/lib/active_support/benchmarkable.rb +++ b/activesupport/lib/active_support/benchmarkable.rb @@ -35,7 +35,7 @@ module ActiveSupport options[:level] ||= :info result = nil - ms = Benchmark.ms { result = options[:silence] ? logger.silence { yield } : yield } + ms = Benchmark.ms { result = options[:silence] ? silence { yield } : yield } logger.send(options[:level], '%s (%.1fms)' % [ message, ms ]) result else diff --git a/activesupport/lib/active_support/buffered_logger.rb b/activesupport/lib/active_support/buffered_logger.rb index 36e29644c6..0595446189 100644 --- a/activesupport/lib/active_support/buffered_logger.rb +++ b/activesupport/lib/active_support/buffered_logger.rb @@ -1,8 +1,5 @@ -require 'thread' -require 'active_support/core_ext/class/attribute_accessors' require 'active_support/deprecation' require 'active_support/logger' -require 'fileutils' module ActiveSupport BufferedLogger = ActiveSupport::Deprecation::DeprecatedConstantProxy.new( diff --git a/activesupport/lib/active_support/cache.rb b/activesupport/lib/active_support/cache.rb index 7d032ca984..b9f196d7a9 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,8 @@ 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 + when key.respond_to?(:to_a) then retrieve_cache_key(key.to_a) else key.to_param end.to_s end @@ -383,11 +383,7 @@ module ActiveSupport options = merged_options(options) instrument(:exist?, name) do |payload| entry = read_entry(namespaced_key(name, options), options) - if entry && !entry.expired? - true - else - false - end + entry && !entry.expired? end end diff --git a/activesupport/lib/active_support/cache/file_store.rb b/activesupport/lib/active_support/cache/file_store.rb index 9460532af0..8e6a3bc5a8 100644 --- a/activesupport/lib/active_support/cache/file_store.rb +++ b/activesupport/lib/active_support/cache/file_store.rb @@ -13,7 +13,7 @@ module ActiveSupport attr_reader :cache_path DIR_FORMATTER = "%03X" - FILENAME_MAX_SIZE = 230 # max filename size on file system is 255, minus room for timestamp and random characters appended by Tempfile (used by atomic write) + FILENAME_MAX_SIZE = 228 # max filename size on file system is 255, minus room for timestamp and random characters appended by Tempfile (used by atomic write) EXCLUDED_DIRS = ['.', '..'].freeze def initialize(cache_path, options = nil) @@ -143,7 +143,7 @@ module ActiveSupport # Translate a file path into a key. def file_path_key(path) - fname = path[cache_path.size, path.size].split(File::SEPARATOR, 4).last + fname = path[cache_path.to_s.size..-1].split(File::SEPARATOR, 4).last Rack::Utils.unescape(fname) end diff --git a/activesupport/lib/active_support/cache/mem_cache_store.rb b/activesupport/lib/active_support/cache/mem_cache_store.rb index e38a8387b4..2e1ccb72d8 100644 --- a/activesupport/lib/active_support/cache/mem_cache_store.rb +++ b/activesupport/lib/active_support/cache/mem_cache_store.rb @@ -6,7 +6,6 @@ rescue LoadError => e end require 'digest/md5' -require 'active_support/core_ext/string/encoding' module ActiveSupport module Cache diff --git a/activesupport/lib/active_support/callbacks.rb b/activesupport/lib/active_support/callbacks.rb index f1d00aab6d..cbeba3139a 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. @@ -76,8 +74,10 @@ module ActiveSupport # save # end # - def run_callbacks(kind, *args, &block) - send("_run_#{kind}_callbacks", *args, &block) + def run_callbacks(kind, key = nil, &block) + #TODO: deprecate key argument + runner_name = self.class.__define_callbacks(kind, self) + send(runner_name, &block) end private @@ -91,29 +91,29 @@ module ActiveSupport class Callback #:nodoc:# @@_callback_sequence = 0 - attr_accessor :chain, :filter, :kind, :options, :per_key, :klass, :raw_filter + attr_accessor :chain, :filter, :kind, :options, :klass, :raw_filter def initialize(chain, filter, kind, options, klass) @chain, @kind, @klass = chain, kind, klass + deprecate_per_key_option(options) normalize_options!(options) - @per_key = options.delete(:per_key) @raw_filter, @options = filter, options @filter = _compile_filter(filter) - @compiled_options = _compile_options(options) - @callback_id = next_id + recompile_options! + end - _compile_per_key_options + 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) obj = super() obj.chain = chain obj.klass = klass - obj.per_key = @per_key.dup obj.options = @options.dup - obj.per_key[:if] = @per_key[:if].dup - obj.per_key[:unless] = @per_key[:unless].dup obj.options[:if] = @options[:if].dup obj.options[:unless] = @options[:unless].dup obj @@ -122,10 +122,6 @@ module ActiveSupport def normalize_options!(options) options[:if] = Array(options[:if]) options[:unless] = Array(options[:unless]) - - options[:per_key] ||= {} - options[:per_key][:if] = Array(options[:per_key][:if]) - options[:per_key][:unless] = Array(options[:per_key][:unless]) end def name @@ -141,32 +137,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.per_key, _per_key) - @callback_id = next_id - @filter = _compile_filter(@raw_filter) - @compiled_options = _compile_options(@options) - _compile_per_key_options - end - - def _compile_per_key_options - key_options = _compile_options(@per_key) - - @klass.class_eval <<-RUBY_EVAL, __FILE__, __LINE__ + 1 - def _one_time_conditions_valid_#{@callback_id}? - true if #{key_options} - end - RUBY_EVAL + recompile_options! end # Wraps code with filter - def apply(code, key=nil, object=nil) + def apply(code) case @kind when :before <<-RUBY_EVAL @@ -185,7 +168,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 @@ -200,17 +183,12 @@ module ActiveSupport end end - - def one_time_conditions_valid?(object) - object.send("_one_time_conditions_valid_#{@callback_id}?") - end - private # Compile around filters with conditions into proxy methods # that contain the conditions. # - # For `around_save :filter_name, :if => :condition': + # For `set_callback :save, :around, :filter_name, :if => :condition': # # def _conditional_callback_save_17 # if condition @@ -241,7 +219,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? @@ -252,7 +230,7 @@ module ActiveSupport conditions << Array(_compile_filter(options[:unless])).map {|f| "!#{f}"} end - conditions.flatten.join(" && ") + @compiled_options = conditions.flatten.join(" && ") end # Filters support: @@ -330,91 +308,51 @@ module ActiveSupport @name = name @config = { :terminator => "false", - :rescuable => false, :scope => [ :kind ] }.merge(config) end - def compile(key=nil, object=nil) + def compile method = [] method << "value = nil" method << "halted = false" - callbacks = yielding - applicable_callbacks_for(key, object).reverse_each do |callback| - callbacks = callback.apply(callbacks, key, object) + callbacks = "value = yield if block_given? && !halted" + reverse_each do |callback| + callbacks = callback.apply(callbacks) end method << callbacks - method << "raise rescued_error if rescued_error" if config[:rescuable] method << "halted ? false : (block_given? ? value : true)" - method.flatten.compact.join("\n") - end - - # Returns part of method that evaluates the callback block - def yielding - method = [] - if config[:rescuable] - method << "rescued_error = nil" - method << "begin" - end - - method << "value = yield if block_given? && !halted" - - if config[:rescuable] - method << "rescue Exception => e" - method << "rescued_error = e" - method << "end" - end method.join("\n") end - # Selects callbacks that have valid <tt>:per_key</tt> condition - def applicable_callbacks_for(key, object) - return self unless key - select do |callback| - callback.one_time_conditions_valid?(object) - end - end end module ClassMethods - # Generate the internal runner method called by +run_callbacks+. - def __define_runner(symbol) #:nodoc: - runner_method = "_run_#{symbol}_callbacks" - unless private_method_defined?(runner_method) - class_eval <<-RUBY_EVAL, __FILE__, __LINE__ + 1 - def #{runner_method}(key = nil, &blk) - self.class.__run_callback(key, :#{symbol}, self, &blk) - end - private :#{runner_method} - RUBY_EVAL - end - end - # 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. - # - def __run_callback(key, kind, object, &blk) #:nodoc: - name = __callback_runner_name(key, kind) - unless object.respond_to?(name) - str = send("_#{kind}_callbacks").compile(key, object) + # This method defines callback chain method for the given kind + # if it was not yet defined. + # This generated method plays caching role. + def __define_callbacks(kind, object) #:nodoc: + name = __callback_runner_name(kind) + unless object.respond_to?(name, true) + str = object.send("_#{kind}_callbacks").compile class_eval <<-RUBY_EVAL, __FILE__, __LINE__ + 1 def #{name}() #{str} end protected :#{name} RUBY_EVAL end - object.send(name, &blk) + name 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) - "_run__#{self.name.hash.abs}__#{kind}__#{key.hash.abs}__callbacks" + def __callback_runner_name(kind) + "_run__#{self.name.hash.abs}__#{kind}__callbacks" end # This is used internally to append, prepend and skip callbacks to the @@ -467,29 +405,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 @@ -524,7 +439,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) @@ -568,10 +483,10 @@ module ActiveSupport # other callbacks are not executed. Defaults to "false", meaning no value # halts the chain. # - # * <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 - # executing all the after callbacks the stored exception is raised. + # * <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>:scope</tt> - Indicates which methods should be executed when an object # is used as a callback. @@ -621,7 +536,6 @@ module ActiveSupport callbacks.each do |callback| class_attribute "_#{callback}_callbacks" send("_#{callback}_callbacks=", CallbackChain.new(callback, config)) - __define_runner(callback) end end end diff --git a/activesupport/lib/active_support/configurable.rb b/activesupport/lib/active_support/configurable.rb index a2d2719de7..a8aa53a80f 100644 --- a/activesupport/lib/active_support/configurable.rb +++ b/activesupport/lib/active_support/configurable.rb @@ -1,7 +1,5 @@ require 'active_support/concern' require 'active_support/ordered_options' -require 'active_support/core_ext/kernel/singleton_class' -require 'active_support/core_ext/module/delegation' require 'active_support/core_ext/array/extract_options' module ActiveSupport diff --git a/activesupport/lib/active_support/core_ext/array/access.rb b/activesupport/lib/active_support/core_ext/array/access.rb index 6162f7af27..44d90ef732 100644 --- a/activesupport/lib/active_support/core_ext/array/access.rb +++ b/activesupport/lib/active_support/core_ext/array/access.rb @@ -16,7 +16,7 @@ class Array # %w( a b c d ).to(10) # => %w( a b c d ) # %w().to(0) # => %w() def to(position) - self.first position + 1 + first position + 1 end # Equal to <tt>self[1]</tt>. diff --git a/activesupport/lib/active_support/core_ext/array/conversions.rb b/activesupport/lib/active_support/core_ext/array/conversions.rb index f3d06ecb2f..24aa28b895 100644 --- a/activesupport/lib/active_support/core_ext/array/conversions.rb +++ b/activesupport/lib/active_support/core_ext/array/conversions.rb @@ -9,28 +9,32 @@ class Array # * <tt>:two_words_connector</tt> - The sign or word used to join the elements in arrays with two elements (default: " and ") # * <tt>:last_word_connector</tt> - The sign or word used to join the last element in arrays with three or more elements (default: ", and ") def to_sentence(options = {}) + options.assert_valid_keys(:words_connector, :two_words_connector, :last_word_connector, :locale) + + default_connectors = { + :words_connector => ', ', + :two_words_connector => ' and ', + :last_word_connector => ', and ' + } if defined?(I18n) - default_words_connector = I18n.translate(:'support.array.words_connector', :locale => options[:locale]) - default_two_words_connector = I18n.translate(:'support.array.two_words_connector', :locale => options[:locale]) - default_last_word_connector = I18n.translate(:'support.array.last_word_connector', :locale => options[:locale]) - else - default_words_connector = ", " - default_two_words_connector = " and " - default_last_word_connector = ", and " + namespace = 'support.array.' + default_connectors.each_key do |name| + i18n_key = (namespace + name.to_s).to_sym + default_connectors[name] = I18n.translate i18n_key, :locale => options[:locale] + end end - options.assert_valid_keys(:words_connector, :two_words_connector, :last_word_connector, :locale) - options.reverse_merge! :words_connector => default_words_connector, :two_words_connector => default_two_words_connector, :last_word_connector => default_last_word_connector + options.reverse_merge! default_connectors case length - when 0 - "" - when 1 - self[0].to_s.dup - when 2 - "#{self[0]}#{options[:two_words_connector]}#{self[1]}" - else - "#{self[0...-1].join(options[:words_connector])}#{options[:last_word_connector]}#{self[-1]}" + when 0 + '' + when 1 + self[0].to_s.dup + when 2 + "#{self[0]}#{options[:two_words_connector]}#{self[1]}" + else + "#{self[0...-1].join(options[:words_connector])}#{options[:last_word_connector]}#{self[-1]}" end end @@ -45,14 +49,14 @@ class Array # Blog.all.to_formatted_s(:db) # => "1,2,3" def to_formatted_s(format = :default) case format - when :db - if respond_to?(:empty?) && self.empty? - "null" - else - collect { |element| element.id }.join(",") - end + when :db + if empty? + 'null' else - to_default_s + collect { |element| element.id }.join(',') + end + else + to_default_s end end alias_method :to_default_s, :to_s @@ -86,20 +90,20 @@ class Array # </project> # </projects> # - # Otherwise the root element is "records": + # Otherwise the root element is "objects": # # [{:foo => 1, :bar => 2}, {:baz => 3}].to_xml # # <?xml version="1.0" encoding="UTF-8"?> - # <records type="array"> - # <record> + # <objects type="array"> + # <object> # <bar type="integer">2</bar> # <foo type="integer">1</foo> - # </record> - # <record> + # </object> + # <object> # <baz type="integer">3</baz> - # </record> - # </records> + # </object> + # </objects> # # If the collection is empty the root element is "nil-classes" by default: # @@ -139,26 +143,28 @@ class Array options = options.dup options[:indent] ||= 2 options[:builder] ||= Builder::XmlMarkup.new(:indent => options[:indent]) - options[:root] ||= if first.class.to_s != "Hash" && all? { |e| e.is_a?(first.class) } - underscored = ActiveSupport::Inflector.underscore(first.class.name) - ActiveSupport::Inflector.pluralize(underscored).tr('/', '_') - else - "objects" - end + options[:root] ||= \ + if first.class != Hash && all? { |e| e.is_a?(first.class) } + underscored = ActiveSupport::Inflector.underscore(first.class.name) + ActiveSupport::Inflector.pluralize(underscored).tr('/', '_') + else + 'objects' + end builder = options[:builder] builder.instruct! unless options.delete(:skip_instruct) root = ActiveSupport::XmlMini.rename_key(options[:root].to_s, options) children = options.delete(:children) || root.singularize + attributes = options[:skip_types] ? {} : {:type => 'array'} - attributes = options[:skip_types] ? {} : {:type => "array"} - return builder.tag!(root, attributes) if empty? - - builder.__send__(:method_missing, root, attributes) do - each { |value| ActiveSupport::XmlMini.to_tag(children, value, options) } - yield builder if block_given? + if empty? + builder.tag!(root, attributes) + else + builder.__send__(:method_missing, root, attributes) do + each { |value| ActiveSupport::XmlMini.to_tag(children, value, options) } + yield builder if block_given? + end end end - end diff --git a/activesupport/lib/active_support/core_ext/array/grouping.rb b/activesupport/lib/active_support/core_ext/array/grouping.rb index 2b3f639cb1..ac1ae53db0 100644 --- a/activesupport/lib/active_support/core_ext/array/grouping.rb +++ b/activesupport/lib/active_support/core_ext/array/grouping.rb @@ -82,11 +82,9 @@ class Array # # [1, 2, 3, 4, 5].split(3) # => [[1, 2], [4, 5]] # (1..10).to_a.split { |i| i % 3 == 0 } # => [[1, 2], [4, 5], [7, 8], [10]] - def split(value = nil) - using_block = block_given? - + def split(value = nil, &block) inject([[]]) do |results, element| - if (using_block && yield(element)) || (value == element) + if block && block.call(element) || value == element results << [] else results.last << element diff --git a/activesupport/lib/active_support/core_ext/array/uniq_by.rb b/activesupport/lib/active_support/core_ext/array/uniq_by.rb index ac3dedc0e3..3bedfa9a61 100644 --- a/activesupport/lib/active_support/core_ext/array/uniq_by.rb +++ b/activesupport/lib/active_support/core_ext/array/uniq_by.rb @@ -6,8 +6,7 @@ class Array # [1, 2, 3, 4].uniq_by { |i| i.odd? } # => [1, 2] # def uniq_by(&block) - ActiveSupport::Deprecation.warn "uniq_by " \ - "is deprecated. Use Array#uniq instead", caller + ActiveSupport::Deprecation.warn 'uniq_by is deprecated. Use Array#uniq instead', caller uniq(&block) end @@ -15,8 +14,7 @@ class Array # # Same as +uniq_by+, but modifies +self+. def uniq_by!(&block) - ActiveSupport::Deprecation.warn "uniq_by! " \ - "is deprecated. Use Array#uniq! instead", caller + ActiveSupport::Deprecation.warn 'uniq_by! is deprecated. Use Array#uniq! instead', caller uniq!(&block) end end diff --git a/activesupport/lib/active_support/core_ext/class/attribute.rb b/activesupport/lib/active_support/core_ext/class/attribute.rb index 305ed4964b..c64685a694 100644 --- a/activesupport/lib/active_support/core_ext/class/attribute.rb +++ b/activesupport/lib/active_support/core_ext/class/attribute.rb @@ -109,7 +109,7 @@ class Class end private - def singleton_class? - ancestors.first != self - end + def singleton_class? + ancestors.first != self + end end diff --git a/activesupport/lib/active_support/core_ext/class/attribute_accessors.rb b/activesupport/lib/active_support/core_ext/class/attribute_accessors.rb index 268303aaf2..5cb528cfe9 100644 --- a/activesupport/lib/active_support/core_ext/class/attribute_accessors.rb +++ b/activesupport/lib/active_support/core_ext/class/attribute_accessors.rb @@ -29,6 +29,7 @@ class Class def cattr_reader(*syms) options = syms.extract_options! syms.each do |sym| + raise NameError.new('invalid attribute name') unless sym =~ /^[_A-Za-z]\w*$/ class_eval(<<-EOS, __FILE__, __LINE__ + 1) unless defined? @@#{sym} @@#{sym} = nil @@ -52,6 +53,7 @@ class Class def cattr_writer(*syms) options = syms.extract_options! syms.each do |sym| + raise NameError.new('invalid attribute name') unless sym =~ /^[_A-Za-z]\w*$/ class_eval(<<-EOS, __FILE__, __LINE__ + 1) unless defined? @@#{sym} @@#{sym} = nil @@ -69,7 +71,7 @@ class Class end EOS end - self.send("#{sym}=", yield) if block_given? + send("#{sym}=", yield) if block_given? end end diff --git a/activesupport/lib/active_support/core_ext/class/delegating_attributes.rb b/activesupport/lib/active_support/core_ext/class/delegating_attributes.rb index 29bf7c0f3d..ff870f5fd1 100644 --- a/activesupport/lib/active_support/core_ext/class/delegating_attributes.rb +++ b/activesupport/lib/active_support/core_ext/class/delegating_attributes.rb @@ -1,5 +1,3 @@ -require 'active_support/core_ext/object/blank' -require 'active_support/core_ext/array/extract_options' require 'active_support/core_ext/kernel/singleton_class' require 'active_support/core_ext/module/remove_method' @@ -22,23 +20,21 @@ class Class define_method("#{name}?") { !!send("#{name}") } if options[:instance_reader] != false end -private - - # Take the object being set and store it in a method. This gives us automatic - # inheritance behavior, without having to store the object in an instance - # variable and look up the superclass chain manually. - def _stash_object_in_method(object, method, instance_reader = true) - singleton_class.remove_possible_method(method) - singleton_class.send(:define_method, method) { object } - remove_possible_method(method) - define_method(method) { object } if instance_reader - end - - def _superclass_delegating_accessor(name, options = {}) - singleton_class.send(:define_method, "#{name}=") do |value| - _stash_object_in_method(value, name, options[:instance_reader] != false) + private + # Take the object being set and store it in a method. This gives us automatic + # inheritance behavior, without having to store the object in an instance + # variable and look up the superclass chain manually. + def _stash_object_in_method(object, method, instance_reader = true) + singleton_class.remove_possible_method(method) + singleton_class.send(:define_method, method) { object } + remove_possible_method(method) + define_method(method) { object } if instance_reader end - send("#{name}=", nil) - end + def _superclass_delegating_accessor(name, options = {}) + singleton_class.send(:define_method, "#{name}=") do |value| + _stash_object_in_method(value, name, options[:instance_reader] != false) + end + send("#{name}=", nil) + end end diff --git a/activesupport/lib/active_support/core_ext/date/calculations.rb b/activesupport/lib/active_support/core_ext/date/calculations.rb index af78226c21..323126fcfa 100644 --- a/activesupport/lib/active_support/core_ext/date/calculations.rb +++ b/activesupport/lib/active_support/core_ext/date/calculations.rb @@ -5,7 +5,15 @@ require 'active_support/core_ext/date/zones' require 'active_support/core_ext/time/zones' class Date - DAYS_INTO_WEEK = { :monday => 0, :tuesday => 1, :wednesday => 2, :thursday => 3, :friday => 4, :saturday => 5, :sunday => 6 } + DAYS_INTO_WEEK = { + :monday => 0, + :tuesday => 1, + :wednesday => 2, + :thursday => 3, + :friday => 4, + :saturday => 5, + :sunday => 6 + } class << self # Returns a new Date representing the date 1 day ago (i.e. yesterday's date). @@ -31,7 +39,7 @@ class Date # Returns true if the Date object's date is today. def today? - self.to_date == ::Date.current # we need the to_date because of DateTime + to_date == ::Date.current # we need the to_date because of DateTime end # Returns true if the Date object's date lies in the future. @@ -105,9 +113,9 @@ class Date # Date.new(2007, 5, 12).change(:year => 2005, :month => 1) # => Date.new(2005, 1, 12) def change(options) ::Date.new( - options[:year] || self.year, - options[:month] || self.month, - options[:day] || self.day + options.fetch(:year, year), + options.fetch(:month, month), + options.fetch(:day, day) ) end @@ -166,7 +174,7 @@ class Date def end_of_week(start_day = :monday) days_to_end = 6 - days_to_week_start(start_day) result = self + days_to_end.days - self.acts_like?(:time) ? result.end_of_day : result + acts_like?(:time) ? result.end_of_day : result end alias :at_end_of_week :end_of_week @@ -180,49 +188,70 @@ class Date # week. Default is +:monday+. +DateTime+ objects have their time set to 0:00. def prev_week(day = :monday) result = (self - 7).beginning_of_week + DAYS_INTO_WEEK[day] - self.acts_like?(:time) ? result.change(:hour => 0) : result + acts_like?(:time) ? result.change(:hour => 0) : result end + alias :last_week :prev_week + + # Alias of prev_month + alias :last_month :prev_month + + # Alias of prev_year + alias :last_year :prev_year # Returns a new Date/DateTime representing the start of the given day in next week (default is :monday). def next_week(day = :monday) result = (self + 7).beginning_of_week + DAYS_INTO_WEEK[day] - self.acts_like?(:time) ? result.change(:hour => 0) : result + acts_like?(:time) ? result.change(:hour => 0) : result end # Returns a new ; DateTime objects will have time set to 0:00DateTime representing the start of the month (1st of the month; DateTime objects will have time set to 0:00) def beginning_of_month - self.acts_like?(:time) ? change(:day => 1, :hour => 0) : change(:day => 1) + acts_like?(:time) ? change(:day => 1, :hour => 0) : change(:day => 1) end alias :at_beginning_of_month :beginning_of_month # Returns a new Date/DateTime representing the end of the month (last day of the month; DateTime objects will have time set to 0:00) def end_of_month - last_day = ::Time.days_in_month( self.month, self.year ) - self.acts_like?(:time) ? change(:day => last_day, :hour => 23, :min => 59, :sec => 59) : change(:day => last_day) + last_day = ::Time.days_in_month(month, year) + if acts_like?(:time) + change(:day => last_day, :hour => 23, :min => 59, :sec => 59) + else + change(:day => last_day) + end end alias :at_end_of_month :end_of_month # Returns a new Date/DateTime representing the start of the quarter (1st of january, april, july, october; DateTime objects will have time set to 0:00) def beginning_of_quarter - beginning_of_month.change(:month => [10, 7, 4, 1].detect { |m| m <= self.month }) + first_quarter_month = [10, 7, 4, 1].detect { |m| m <= month } + beginning_of_month.change(:month => first_quarter_month) end alias :at_beginning_of_quarter :beginning_of_quarter # Returns a new Date/DateTime representing the end of the quarter (last day of march, june, september, december; DateTime objects will have time set to 23:59:59) def end_of_quarter - beginning_of_month.change(:month => [3, 6, 9, 12].detect { |m| m >= self.month }).end_of_month + last_quarter_month = [3, 6, 9, 12].detect { |m| m >= month } + beginning_of_month.change(:month => last_quarter_month).end_of_month end alias :at_end_of_quarter :end_of_quarter # Returns a new Date/DateTime representing the start of the year (1st of january; DateTime objects will have time set to 0:00) def beginning_of_year - self.acts_like?(:time) ? change(:month => 1, :day => 1, :hour => 0) : change(:month => 1, :day => 1) + if acts_like?(:time) + change(:month => 1, :day => 1, :hour => 0) + else + change(:month => 1, :day => 1) + end end alias :at_beginning_of_year :beginning_of_year # Returns a new Time representing the end of the year (31st of december; DateTime objects will have time set to 23:59:59) def end_of_year - self.acts_like?(:time) ? change(:month => 12, :day => 31, :hour => 23, :min => 59, :sec => 59) : change(:month => 12, :day => 31) + if acts_like?(:time) + change(:month => 12, :day => 31, :hour => 23, :min => 59, :sec => 59) + else + change(:month => 12, :day => 31) + end end alias :at_end_of_year :end_of_year diff --git a/activesupport/lib/active_support/core_ext/date/conversions.rb b/activesupport/lib/active_support/core_ext/date/conversions.rb index 3262c254f7..97e3c71992 100644 --- a/activesupport/lib/active_support/core_ext/date/conversions.rb +++ b/activesupport/lib/active_support/core_ext/date/conversions.rb @@ -5,12 +5,15 @@ require 'active_support/core_ext/module/remove_method' class Date DATE_FORMATS = { - :short => "%e %b", - :long => "%B %e, %Y", - :db => "%Y-%m-%d", - :number => "%Y%m%d", - :long_ordinal => lambda { |date| date.strftime("%B #{ActiveSupport::Inflector.ordinalize(date.day)}, %Y") }, # => "April 25th, 2007" - :rfc822 => "%e %b %Y" + :short => '%e %b', + :long => '%B %e, %Y', + :db => '%Y-%m-%d', + :number => '%Y%m%d', + :long_ordinal => lambda { |date| + day_format = ActiveSupport::Inflector.ordinalize(date.day) + date.strftime("%B #{day_format}, %Y") # => "April 25th, 2007" + }, + :rfc822 => '%e %b %Y' } # Ruby 1.9 has Date#to_time which converts to localtime only. @@ -40,7 +43,7 @@ class Date # or Proc instance that takes a date argument as the value. # # # config/initializers/time_formats.rb - # Date::DATE_FORMATS[:month_and_year] = "%B %Y" + # Date::DATE_FORMATS[:month_and_year] = '%B %Y' # Date::DATE_FORMATS[:short_ordinal] = lambda { |date| date.strftime("%B #{date.day.ordinalize}") } def to_formatted_s(format = :default) if formatter = DATE_FORMATS[format] @@ -58,7 +61,7 @@ class Date # Overrides the default inspect method with a human readable one, e.g., "Mon, 21 Feb 2005" def readable_inspect - strftime("%a, %d %b %Y") + strftime('%a, %d %b %Y') end alias_method :default_inspect, :inspect alias_method :inspect, :readable_inspect 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 1a3cf66a1b..0e5aa5af10 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>. @@ -31,14 +35,14 @@ class DateTime # minute is passed, then sec is set to 0. def change(options) ::DateTime.civil( - options[:year] || year, - options[:month] || month, - options[:day] || day, - options[:hour] || hour, - options[:min] || (options[:hour] ? 0 : min), - options[:sec] || ((options[:hour] || options[:min]) ? 0 : sec), - options[:offset] || offset, - options[:start] || start + options.fetch(:year, year), + options.fetch(:month, month), + options.fetch(:day, day), + options.fetch(:hour, hour), + options.fetch(:min, options[:hour] ? 0 : min), + options.fetch(:sec, (options[:hour] || options[:min]) ? 0 : sec), + options.fetch(:offset, offset), + options.fetch(:start, start) ) end @@ -49,8 +53,16 @@ class DateTime def advance(options) d = to_date.advance(options) datetime_advanced_by_date = change(:year => d.year, :month => d.month, :day => d.day) - seconds_to_advance = (options[:seconds] || 0) + (options[:minutes] || 0) * 60 + (options[:hours] || 0) * 3600 - seconds_to_advance == 0 ? datetime_advanced_by_date : datetime_advanced_by_date.since(seconds_to_advance) + seconds_to_advance = \ + options.fetch(:seconds, 0) + + options.fetch(:minutes, 0) * 60 + + options.fetch(:hours, 0) * 3600 + + if seconds_to_advance.zero? + datetime_advanced_by_date + else + datetime_advanced_by_date.since seconds_to_advance + end end # Returns a new DateTime representing the time a number of seconds ago @@ -79,29 +91,6 @@ class DateTime change(:hour => 23, :min => 59, :sec => 59) end - # 1.9.3 defines + and - on DateTime, < 1.9.3 do not. - if DateTime.public_instance_methods(false).include?(:+) - def plus_with_duration(other) #:nodoc: - if ActiveSupport::Duration === other - other.since(self) - else - plus_without_duration(other) - end - end - alias_method :plus_without_duration, :+ - alias_method :+, :plus_with_duration - - def minus_with_duration(other) #:nodoc: - if ActiveSupport::Duration === other - plus_with_duration(-other) - else - minus_without_duration(other) - end - end - alias_method :minus_without_duration, :- - alias_method :-, :minus_with_duration - end - # Adjusts DateTime to UTC by adding its offset value; offset is set to 0 # # Example: @@ -127,4 +116,5 @@ class DateTime def <=>(other) super other.to_datetime end + end 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..6338dc6397 100644 --- a/activesupport/lib/active_support/core_ext/date_time/conversions.rb +++ b/activesupport/lib/active_support/core_ext/date_time/conversions.rb @@ -30,7 +30,7 @@ class DateTime # datetime argument as the value. # # # config/initializers/time_formats.rb - # Time::DATE_FORMATS[:month_and_year] = "%B %Y" + # Time::DATE_FORMATS[:month_and_year] = '%B %Y' # Time::DATE_FORMATS[:short_ordinal] = lambda { |time| time.strftime("%B #{time.day.ordinalize}") } def to_formatted_s(format = :default) if formatter = ::Time::DATE_FORMATS[format] @@ -58,32 +58,31 @@ 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 + if offset == 0 + ::Time.utc_time(year, month, day, hour, min, sec, sec_fraction * 1000000) + else + self + end 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/date_time/zones.rb b/activesupport/lib/active_support/core_ext/date_time/zones.rb index 6fa55a9255..823735d3e2 100644 --- a/activesupport/lib/active_support/core_ext/date_time/zones.rb +++ b/activesupport/lib/active_support/core_ext/date_time/zones.rb @@ -14,8 +14,10 @@ class DateTime # # DateTime.new(2000).in_time_zone('Alaska') # => Fri, 31 Dec 1999 15:00:00 AKST -09:00 def in_time_zone(zone = ::Time.zone) - return self unless zone - - ActiveSupport::TimeWithZone.new(utc? ? self : getutc, ::Time.find_zone!(zone)) + if zone + ActiveSupport::TimeWithZone.new(utc? ? self : getutc, ::Time.find_zone!(zone)) + else + self + end end end diff --git a/activesupport/lib/active_support/core_ext/enumerable.rb b/activesupport/lib/active_support/core_ext/enumerable.rb index 77a5087981..b9c632e4f5 100644 --- a/activesupport/lib/active_support/core_ext/enumerable.rb +++ b/activesupport/lib/active_support/core_ext/enumerable.rb @@ -11,7 +11,7 @@ module Enumerable # It can also calculate the sum without the use of a block. # # [5, 15, 10].sum # => 30 - # ["foo", "bar"].sum # => "foobar" + # ['foo', 'bar'].sum # => "foobar" # [[1, 2], [3, 1, 5]].sum => [1, 2, 3, 1, 5] # # The default sum of an empty list is zero. You can override this default: @@ -34,8 +34,11 @@ module Enumerable # => { "Chade- Fowlersburg-e" => <Person ...>, "David Heinemeier Hansson" => <Person ...>, ...} # def index_by - return to_enum :index_by unless block_given? - Hash[map { |elem| [yield(elem), elem] }] + if block_given? + Hash[map { |elem| [yield(elem), elem] }] + else + to_enum :index_by + end end # Returns true if the enumerable has more than 1 element. Functionally equivalent to enum.to_a.size > 1. @@ -48,7 +51,7 @@ module Enumerable cnt > 1 end else - any?{ (cnt += 1) > 1 } + any? { (cnt += 1) > 1 } end end @@ -62,8 +65,11 @@ class Range #:nodoc: # Optimize range sum to use arithmetic progression if a block is not given and # we have a range of numeric values. def sum(identity = 0) - return super if block_given? || !(first.instance_of?(Integer) && last.instance_of?(Integer)) - actual_last = exclude_end? ? (last - 1) : last - (actual_last - first + 1) * (actual_last + first) / 2 + if block_given? || !(first.instance_of?(Integer) && last.instance_of?(Integer)) + super + else + actual_last = exclude_end? ? (last - 1) : last + (actual_last - first + 1) * (actual_last + first) / 2 + end end end diff --git a/activesupport/lib/active_support/core_ext/file/atomic.rb b/activesupport/lib/active_support/core_ext/file/atomic.rb index 3645597301..9e504851e7 100644 --- a/activesupport/lib/active_support/core_ext/file/atomic.rb +++ b/activesupport/lib/active_support/core_ext/file/atomic.rb @@ -2,21 +2,22 @@ class File # Write to a file atomically. Useful for situations where you don't # want other processes or threads to see half-written files. # - # File.atomic_write("important.file") do |file| - # file.write("hello") + # File.atomic_write('important.file') do |file| + # file.write('hello') # end # # If your temp directory is not on the same filesystem as the file you're # trying to write, you can provide a different temporary directory. # - # File.atomic_write("/data/something.important", "/data/tmp") do |file| - # file.write("hello") + # File.atomic_write('/data/something.important', '/data/tmp') do |file| + # file.write('hello') # end def self.atomic_write(file_name, temp_dir = Dir.tmpdir) require 'tempfile' unless defined?(Tempfile) require 'fileutils' unless defined?(FileUtils) temp_file = Tempfile.new(basename(file_name), temp_dir) + temp_file.binmode yield temp_file temp_file.close @@ -25,8 +26,14 @@ class File old_stat = stat(file_name) rescue Errno::ENOENT # No old permissions, write a temp file to determine the defaults - check_name = join(dirname(file_name), ".permissions_check.#{Thread.current.object_id}.#{Process.pid}.#{rand(1000000)}") - open(check_name, "w") { } + temp_file_name = [ + '.permissions_check', + Thread.current.object_id, + Process.pid, + rand(1000000) + ].join('.') + check_name = join(dirname(file_name), temp_file_name) + open(check_name, 'w') { } old_stat = stat(check_name) unlink(check_name) end diff --git a/activesupport/lib/active_support/core_ext/hash/conversions.rb b/activesupport/lib/active_support/core_ext/hash/conversions.rb index 5f07bb4f5a..469dc41f2d 100644 --- a/activesupport/lib/active_support/core_ext/hash/conversions.rb +++ b/activesupport/lib/active_support/core_ext/hash/conversions.rb @@ -8,7 +8,7 @@ require 'active_support/core_ext/string/inflections' class Hash # Returns a string containing an XML representation of its receiver: # - # {"foo" => 1, "bar" => 2}.to_xml + # {'foo' => 1, 'bar' => 2}.to_xml # # => # # <?xml version="1.0" encoding="UTF-8"?> # # <hash> @@ -26,20 +26,20 @@ class Hash # # * If +value+ is a callable object it must expect one or two arguments. Depending # on the arity, the callable is invoked with the +options+ hash as first argument - # with +key+ as <tt>:root</tt>, and +key+ singularized as second argument. The + # with +key+ as <tt>:root</tt>, and +key+ singularized as second argument. The # callable can add nodes by using <tt>options[:builder]</tt>. # - # "foo".to_xml(lambda { |options, key| options[:builder].b(key) }) + # 'foo'.to_xml(lambda { |options, key| options[:builder].b(key) }) # # => "<b>foo</b>" # # * If +value+ responds to +to_xml+ the method is invoked with +key+ as <tt>:root</tt>. - # + # # class Foo # def to_xml(options) - # options[:builder].bar "fooing!" + # options[:builder].bar 'fooing!' # end # end - # + # # {:foo => Foo.new}.to_xml(:skip_instruct => true) # # => "<hash><bar>fooing!</bar></hash>" # @@ -71,7 +71,7 @@ class Hash options = options.dup options[:indent] ||= 2 - options[:root] ||= "hash" + options[:root] ||= 'hash' options[:builder] ||= Builder::XmlMarkup.new(:indent => options[:indent]) builder = options[:builder] @@ -100,24 +100,24 @@ class Hash [] else case entries.class.to_s # something weird with classes not matching here. maybe singleton methods breaking is_a? - when "Array" + when 'Array' entries.collect { |v| typecast_xml_value(v) } - when "Hash" + when 'Hash' [typecast_xml_value(entries)] else raise "can't typecast #{entries.inspect}" end end - elsif value['type'] == 'file' || - (value["__content__"] && (value.keys.size == 1 || value["__content__"].present?)) - content = value["__content__"] - if parser = ActiveSupport::XmlMini::PARSING[value["type"]] + elsif value['type'] == 'file' || + (value['__content__'] && (value.keys.size == 1 || value['__content__'].present?)) + content = value['__content__'] + if parser = ActiveSupport::XmlMini::PARSING[value['type']] parser.arity == 1 ? parser.call(content) : parser.call(content, value) else content end elsif value['type'] == 'string' && value['nil'] != 'true' - "" + '' # blank or nil parsed values are represented by nil elsif value.blank? || value['nil'] == 'true' nil @@ -131,7 +131,7 @@ class Hash # Turn { :files => { :file => #<StringIO> } into { :files => #<StringIO> } so it is compatible with # how multipart uploaded files from HTML appear - xml_value["file"].is_a?(StringIO) ? xml_value["file"] : xml_value + xml_value['file'].is_a?(StringIO) ? xml_value['file'] : xml_value end when 'Array' value.map! { |i| typecast_xml_value(i) } @@ -145,9 +145,9 @@ class Hash def unrename_keys(params) case params.class.to_s - when "Hash" - Hash[params.map { |k,v| [k.to_s.tr("-", "_"), unrename_keys(v)] } ] - when "Array" + when 'Hash' + Hash[params.map { |k,v| [k.to_s.tr('-', '_'), unrename_keys(v)] } ] + when 'Array' params.map { |v| unrename_keys(v) } else params diff --git a/activesupport/lib/active_support/core_ext/hash/deep_dup.rb b/activesupport/lib/active_support/core_ext/hash/deep_dup.rb index 447142605c..9ab179c566 100644 --- a/activesupport/lib/active_support/core_ext/hash/deep_dup.rb +++ b/activesupport/lib/active_support/core_ext/hash/deep_dup.rb @@ -3,8 +3,7 @@ class Hash def deep_dup duplicate = self.dup duplicate.each_pair do |k,v| - tv = duplicate[k] - duplicate[k] = tv.is_a?(Hash) && v.is_a?(Hash) ? tv.deep_dup : v + duplicate[k] = v.is_a?(Hash) ? v.deep_dup : v end duplicate end diff --git a/activesupport/lib/active_support/core_ext/hash/diff.rb b/activesupport/lib/active_support/core_ext/hash/diff.rb index b904f49fa8..855dcb38bc 100644 --- a/activesupport/lib/active_support/core_ext/hash/diff.rb +++ b/activesupport/lib/active_support/core_ext/hash/diff.rb @@ -7,7 +7,9 @@ class Hash # {1 => 2}.diff(1 => 3) # => {1 => 2} # {}.diff(1 => 2) # => {1 => 2} # {1 => 2, 3 => 4}.diff(1 => 2) # => {3 => 4} - def diff(h2) - dup.delete_if { |k, v| h2[k] == v }.merge!(h2.dup.delete_if { |k, v| has_key?(k) }) + def diff(other) + dup. + delete_if { |k, v| other[k] == v }. + merge!(other.dup.delete_if { |k, v| has_key?(k) }) end end diff --git a/activesupport/lib/active_support/core_ext/hash/except.rb b/activesupport/lib/active_support/core_ext/hash/except.rb index 89729df258..5a61906222 100644 --- a/activesupport/lib/active_support/core_ext/hash/except.rb +++ b/activesupport/lib/active_support/core_ext/hash/except.rb @@ -9,7 +9,7 @@ class Hash # for instance: # # {:a => 1}.with_indifferent_access.except(:a) # => {} - # {:a => 1}.with_indifferent_access.except("a") # => {} + # {:a => 1}.with_indifferent_access.except('a') # => {} # def except(*keys) dup.except!(*keys) diff --git a/activesupport/lib/active_support/core_ext/hash/indifferent_access.rb b/activesupport/lib/active_support/core_ext/hash/indifferent_access.rb index f4cb445444..7d54c9fae6 100644 --- a/activesupport/lib/active_support/core_ext/hash/indifferent_access.rb +++ b/activesupport/lib/active_support/core_ext/hash/indifferent_access.rb @@ -14,7 +14,7 @@ class Hash # #with_indifferent_access. This method will be called on the current object # by the enclosing object and is aliased to #with_indifferent_access by # default. Subclasses of Hash may overwrite this method to return +self+ if - # converting to an +ActiveSupport::HashWithIndifferentAccess+ would not be + # converting to an <tt>ActiveSupport::HashWithIndifferentAccess</tt> would not be # desirable. # # b = {:b => 1} diff --git a/activesupport/lib/active_support/core_ext/hash/keys.rb b/activesupport/lib/active_support/core_ext/hash/keys.rb index d8748b1138..230a84dabc 100644 --- a/activesupport/lib/active_support/core_ext/hash/keys.rb +++ b/activesupport/lib/active_support/core_ext/hash/keys.rb @@ -1,7 +1,11 @@ class Hash # Return a new hash with all keys converted to strings. def stringify_keys - dup.stringify_keys! + result = {} + keys.each do |key| + result[key.to_s] = self[key] + end + result end # Destructively convert all keys to strings. @@ -15,19 +19,22 @@ class Hash # Return a new hash with all keys converted to symbols, as long as # they respond to +to_sym+. def symbolize_keys - dup.symbolize_keys! + result = {} + keys.each do |key| + result[(key.to_sym rescue key)] = self[key] + end + result end + alias_method :to_options, :symbolize_keys # Destructively convert all keys to symbols, as long as they respond # to +to_sym+. def symbolize_keys! keys.each do |key| - self[(key.to_sym rescue key) || key] = delete(key) + self[(key.to_sym rescue key)] = delete(key) end self end - - alias_method :to_options, :symbolize_keys alias_method :to_options!, :symbolize_keys! # Validate all keys in a hash match *valid keys, raising ArgumentError on a mismatch. @@ -35,13 +42,13 @@ class Hash # as keys, this will fail. # # ==== Examples - # { :name => "Rob", :years => "28" }.assert_valid_keys(:name, :age) # => raises "ArgumentError: Unknown key: years" - # { :name => "Rob", :age => "28" }.assert_valid_keys("name", "age") # => raises "ArgumentError: Unknown key: name" - # { :name => "Rob", :age => "28" }.assert_valid_keys(:name, :age) # => passes, raises nothing + # { :name => 'Rob', :years => '28' }.assert_valid_keys(:name, :age) # => raises "ArgumentError: Unknown key: years" + # { :name => 'Rob', :age => '28' }.assert_valid_keys('name', 'age') # => raises "ArgumentError: Unknown key: name" + # { :name => 'Rob', :age => '28' }.assert_valid_keys(:name, :age) # => passes, raises nothing def assert_valid_keys(*valid_keys) valid_keys.flatten! each_key do |k| - raise(ArgumentError, "Unknown key: #{k}") unless valid_keys.include?(k) + raise ArgumentError.new("Unknown key: #{k}") unless valid_keys.include?(k) end end end diff --git a/activesupport/lib/active_support/core_ext/hash/reverse_merge.rb b/activesupport/lib/active_support/core_ext/hash/reverse_merge.rb index 01863a162b..6074103484 100644 --- a/activesupport/lib/active_support/core_ext/hash/reverse_merge.rb +++ b/activesupport/lib/active_support/core_ext/hash/reverse_merge.rb @@ -18,6 +18,5 @@ class Hash # right wins if there is no left merge!( other_hash ){|key,left,right| left } end - alias_method :reverse_update, :reverse_merge! end diff --git a/activesupport/lib/active_support/core_ext/hash/slice.rb b/activesupport/lib/active_support/core_ext/hash/slice.rb index 0484d8e5d8..b862b5ae2a 100644 --- a/activesupport/lib/active_support/core_ext/hash/slice.rb +++ b/activesupport/lib/active_support/core_ext/hash/slice.rb @@ -13,17 +13,15 @@ class Hash # valid_keys = [:mass, :velocity, :time] # search(options.slice(*valid_keys)) def slice(*keys) - keys = keys.map! { |key| convert_key(key) } if respond_to?(:convert_key) - hash = self.class.new - keys.each { |k| hash[k] = self[k] if has_key?(k) } - hash + keys.map! { |key| convert_key(key) } if respond_to?(:convert_key, true) + keys.each_with_object(self.class.new) { |k, hash| hash[k] = self[k] if has_key?(k) } end # Replaces the hash with only the given keys. - # Returns a hash contained the removed key/value pairs + # Returns a hash containing the removed key/value pairs. # {:a => 1, :b => 2, :c => 3, :d => 4}.slice!(:a, :b) # => {:c => 3, :d => 4} def slice!(*keys) - keys = keys.map! { |key| convert_key(key) } if respond_to?(:convert_key) + keys.map! { |key| convert_key(key) } if respond_to?(:convert_key, true) omit = slice(*self.keys - keys) hash = slice(*keys) replace(hash) @@ -33,8 +31,6 @@ class Hash # Removes and returns the key/value pairs matching the given keys. # {:a => 1, :b => 2, :c => 3, :d => 4}.extract!(:a, :b) # => {:a => 1, :b => 2} def extract!(*keys) - result = {} - keys.each {|key| result[key] = delete(key) } - result + keys.each_with_object({}) { |key, result| result[key] = delete(key) } end end 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/integer/time.rb b/activesupport/lib/active_support/core_ext/integer/time.rb index c677400396..894b5d0696 100644 --- a/activesupport/lib/active_support/core_ext/integer/time.rb +++ b/activesupport/lib/active_support/core_ext/integer/time.rb @@ -24,9 +24,9 @@ class Integer # 1.year.to_f.from_now # # In such cases, Ruby's core - # Date[http://stdlib.rubyonrails.org/libdoc/date/rdoc/index.html] and - # Time[http://stdlib.rubyonrails.org/libdoc/time/rdoc/index.html] should be used for precision - # date and time arithmetic + # Date[http://ruby-doc.org/stdlib/libdoc/date/rdoc/Date.html] and + # Time[http://ruby-doc.org/stdlib/libdoc/time/rdoc/Time.html] should be used for precision + # date and time arithmetic. def months ActiveSupport::Duration.new(self * 30.days, [[:months, self]]) end diff --git a/activesupport/lib/active_support/core_ext/kernel/debugger.rb b/activesupport/lib/active_support/core_ext/kernel/debugger.rb index d5b590e9f0..2073cac98d 100644 --- a/activesupport/lib/active_support/core_ext/kernel/debugger.rb +++ b/activesupport/lib/active_support/core_ext/kernel/debugger.rb @@ -1,8 +1,8 @@ module Kernel unless respond_to?(:debugger) - # Starts a debugging session if ruby-debug has been loaded (call rails server --debugger to do load it). + # Starts a debugging session if the +debugger+ gem has been loaded (call rails server --debugger to do load it). def debugger - message = "\n***** Debugger requested, but was not available (ensure ruby-debug19 is listed in Gemfile/installed as gem): Start server with --debugger to enable *****\n" + message = "\n***** Debugger requested, but was not available (ensure the debugger gem is listed in Gemfile/installed as gem): Start server with --debugger to enable *****\n" defined?(Rails) ? Rails.logger.info(message) : $stderr.puts(message) end alias breakpoint debugger unless respond_to?(:breakpoint) diff --git a/activesupport/lib/active_support/core_ext/kernel/reporting.rb b/activesupport/lib/active_support/core_ext/kernel/reporting.rb index 526b8378a5..ad3f9ebec9 100644 --- a/activesupport/lib/active_support/core_ext/kernel/reporting.rb +++ b/activesupport/lib/active_support/core_ext/kernel/reporting.rb @@ -1,4 +1,5 @@ require 'rbconfig' + module Kernel # Sets $VERBOSE to nil for the duration of the block and back to its original value afterwards. # @@ -49,10 +50,10 @@ module Kernel # # suppress(ZeroDivisionError) do # 1/0 - # puts "This code is NOT reached" + # puts 'This code is NOT reached' # end # - # puts "This code gets executed and nothing related to ZeroDivisionError was seen" + # puts 'This code gets executed and nothing related to ZeroDivisionError was seen' def suppress(*exception_classes) begin yield rescue Exception => e @@ -62,7 +63,7 @@ module Kernel # Captures the given stream and returns it: # - # stream = capture(:stdout) { puts "Cool" } + # stream = capture(:stdout) { puts 'Cool' } # stream # => "Cool\n" # def capture(stream) diff --git a/activesupport/lib/active_support/core_ext/logger.rb b/activesupport/lib/active_support/core_ext/logger.rb index a51818d2b2..16fce81445 100644 --- a/activesupport/lib/active_support/core_ext/logger.rb +++ b/activesupport/lib/active_support/core_ext/logger.rb @@ -56,8 +56,8 @@ class Logger alias :old_datetime_format= :datetime_format= # Logging date-time format (string passed to +strftime+). Ignored if the formatter # does not respond to datetime_format=. - def datetime_format=(datetime_format) - formatter.datetime_format = datetime_format if formatter.respond_to?(:datetime_format=) + def datetime_format=(format) + formatter.datetime_format = format if formatter.respond_to?(:datetime_format=) end alias :old_datetime_format :datetime_format diff --git a/activesupport/lib/active_support/core_ext/module/aliasing.rb b/activesupport/lib/active_support/core_ext/module/aliasing.rb index ce481f0e84..382156ecd8 100644 --- a/activesupport/lib/active_support/core_ext/module/aliasing.rb +++ b/activesupport/lib/active_support/core_ext/module/aliasing.rb @@ -26,18 +26,19 @@ class Module aliased_target, punctuation = target.to_s.sub(/([?!=])$/, ''), $1 yield(aliased_target, punctuation) if block_given? - with_method, without_method = "#{aliased_target}_with_#{feature}#{punctuation}", "#{aliased_target}_without_#{feature}#{punctuation}" + with_method = "#{aliased_target}_with_#{feature}#{punctuation}" + without_method = "#{aliased_target}_without_#{feature}#{punctuation}" alias_method without_method, target alias_method target, with_method case - when public_method_defined?(without_method) - public target - when protected_method_defined?(without_method) - protected target - when private_method_defined?(without_method) - private target + when public_method_defined?(without_method) + public target + when protected_method_defined?(without_method) + protected target + when private_method_defined?(without_method) + private target end end diff --git a/activesupport/lib/active_support/core_ext/module/attr_internal.rb b/activesupport/lib/active_support/core_ext/module/attr_internal.rb index 00db75bfec..db07d549b0 100644 --- a/activesupport/lib/active_support/core_ext/module/attr_internal.rb +++ b/activesupport/lib/active_support/core_ext/module/attr_internal.rb @@ -15,7 +15,6 @@ class Module attr_internal_reader(*attrs) attr_internal_writer(*attrs) end - alias_method :attr_internal, :attr_internal_accessor class << self; attr_accessor :attr_internal_naming_format end 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 be94ae1565..f914425827 100644 --- a/activesupport/lib/active_support/core_ext/module/attribute_accessors.rb +++ b/activesupport/lib/active_support/core_ext/module/attribute_accessors.rb @@ -4,6 +4,7 @@ class Module def mattr_reader(*syms) options = syms.extract_options! syms.each do |sym| + raise NameError.new('invalid attribute name') unless sym =~ /^[_A-Za-z]\w*$/ class_eval(<<-EOS, __FILE__, __LINE__ + 1) @@#{sym} = nil unless defined? @@#{sym} @@ -25,6 +26,7 @@ class Module def mattr_writer(*syms) options = syms.extract_options! syms.each do |sym| + raise NameError.new('invalid attribute name') unless sym =~ /^[_A-Za-z]\w*$/ class_eval(<<-EOS, __FILE__, __LINE__ + 1) def self.#{sym}=(obj) @@#{sym} = obj diff --git a/activesupport/lib/active_support/core_ext/module/delegation.rb b/activesupport/lib/active_support/core_ext/module/delegation.rb index 7de824a77f..fbef27c76a 100644 --- a/activesupport/lib/active_support/core_ext/module/delegation.rb +++ b/activesupport/lib/active_support/core_ext/module/delegation.rb @@ -1,5 +1,5 @@ class Module - # Provides a delegate class method to easily expose contained objects' methods + # Provides a delegate class method to easily expose contained objects' public methods # as your own. Pass one or more methods (specified as symbols or strings) # and the name of the target object via the <tt>:to</tt> option (also a symbol # or string). At least one method and the <tt>:to</tt> option are required. @@ -8,11 +8,11 @@ class Module # # class Greeter < ActiveRecord::Base # def hello - # "hello" + # 'hello' # end # # def goodbye - # "goodbye" + # 'goodbye' # end # end # @@ -62,7 +62,7 @@ class Module # delegate :name, :address, :to => :client, :prefix => true # end # - # john_doe = Person.new("John Doe", "Vimmersvej 13") + # john_doe = Person.new('John Doe', 'Vimmersvej 13') # invoice = Invoice.new(john_doe) # invoice.client_name # => "John Doe" # invoice.client_address # => "Vimmersvej 13" @@ -74,45 +74,47 @@ class Module # end # # invoice = Invoice.new(john_doe) - # invoice.customer_name # => "John Doe" - # invoice.customer_address # => "Vimmersvej 13" + # invoice.customer_name # => 'John Doe' + # invoice.customer_address # => 'Vimmersvej 13' # # If the delegate object is +nil+ an exception is raised, and that happens # 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 + # 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) + # 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 + # 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 + # Foo.new.zoo # returns nil # def delegate(*methods) options = methods.pop unless options.is_a?(Hash) && to = options[:to] - raise ArgumentError, "Delegation needs a target. Supply an options hash with a :to key as the last argument (e.g. delegate :hello, :to => :greeter)." + raise ArgumentError, 'Delegation needs a target. Supply an options hash with a :to key as the last argument (e.g. delegate :hello, :to => :greeter).' end - prefix, to, allow_nil = options[:prefix], options[:to], options[:allow_nil] - if prefix == true && to.to_s =~ /^[^a-z_]/ - raise ArgumentError, "Can only automatically set the delegation prefix when delegating to a method." + to = to.to_s + prefix, allow_nil = options.values_at(:prefix, :allow_nil) + + if prefix == true && to =~ /^[^a-z_]/ + raise ArgumentError, 'Can only automatically set the delegation prefix when delegating to a method.' end - method_prefix = + method_prefix = \ if prefix "#{prefix == true ? to : prefix}_" else @@ -125,11 +127,15 @@ class Module methods.each do |method| method = method.to_s + # Attribute writer methods only accept one argument. Makes sure []= + # methods still accept two arguments. + definition = (method =~ /[^\]]=$/) ? 'arg' : '*args, &block' + if allow_nil module_eval(<<-EOS, file, line - 2) - def #{method_prefix}#{method}(*args, &block) # def customer_name(*args, &block) + def #{method_prefix}#{method}(#{definition}) # def customer_name(*args, &block) if #{to} || #{to}.respond_to?(:#{method}) # if client || client.respond_to?(:name) - #{to}.__send__(:#{method}, *args, &block) # client.__send__(:name, *args, &block) + #{to}.#{method}(#{definition}) # client.name(*args, &block) end # end end # end EOS @@ -137,8 +143,8 @@ class Module exception = %(raise "#{self}##{method_prefix}#{method} delegated to #{to}.#{method}, but #{to} is nil: \#{self.inspect}") module_eval(<<-EOS, file, line - 1) - def #{method_prefix}#{method}(*args, &block) # def customer_name(*args, &block) - #{to}.__send__(:#{method}, *args, &block) # client.__send__(:name, *args, &block) + def #{method_prefix}#{method}(#{definition}) # def customer_name(*args, &block) + #{to}.#{method}(#{definition}) # client.name(*args, &block) rescue NoMethodError # rescue NoMethodError if #{to}.nil? # if client.nil? #{exception} # # add helpful message to the exception diff --git a/activesupport/lib/active_support/core_ext/module/deprecation.rb b/activesupport/lib/active_support/core_ext/module/deprecation.rb index 5a5b4e3f80..9e77ac3c45 100644 --- a/activesupport/lib/active_support/core_ext/module/deprecation.rb +++ b/activesupport/lib/active_support/core_ext/module/deprecation.rb @@ -1,3 +1,5 @@ +require 'active_support/deprecation/method_wrappers' + class Module # Declare that a method has been deprecated. # deprecate :foo diff --git a/activesupport/lib/active_support/core_ext/module/introspection.rb b/activesupport/lib/active_support/core_ext/module/introspection.rb index 1893a9cfa6..3c8e811fa4 100644 --- a/activesupport/lib/active_support/core_ext/module/introspection.rb +++ b/activesupport/lib/active_support/core_ext/module/introspection.rb @@ -5,10 +5,11 @@ class Module # # M::N.parent_name # => "M" def parent_name - unless defined? @parent_name + if defined? @parent_name + @parent_name + else @parent_name = name =~ /::[^:]+\Z/ ? $`.freeze : nil end - @parent_name end # Returns the module which contains this one according to its name. @@ -61,9 +62,19 @@ class Module constants(false) end - # Returns the names of the constants defined locally rather than the - # constants themselves. See <tt>local_constants</tt>. + # *DEPRECATED*: Use +local_constants+ instead. + # + # Returns the names of the constants defined locally as strings. + # + # module M + # X = 1 + # end + # M.local_constant_names # => ["X"] + # + # This method is useful for forward compatibility, since Ruby 1.8 returns + # constant names as strings, whereas 1.9 returns them as symbols. def local_constant_names + ActiveSupport::Deprecation.warn 'Module#local_constant_names is deprecated, use Module#local_constants instead', caller local_constants.map { |c| c.to_s } end end diff --git a/activesupport/lib/active_support/core_ext/module/qualified_const.rb b/activesupport/lib/active_support/core_ext/module/qualified_const.rb index 8adf050b6b..65525013db 100644 --- a/activesupport/lib/active_support/core_ext/module/qualified_const.rb +++ b/activesupport/lib/active_support/core_ext/module/qualified_const.rb @@ -5,7 +5,7 @@ require 'active_support/core_ext/string/inflections' #++ module QualifiedConstUtils def self.raise_if_absolute(path) - raise NameError, "wrong constant name #$&" if path =~ /\A::[^:]+/ + raise NameError.new("wrong constant name #$&") if path =~ /\A::[^:]+/ end def self.names(path) @@ -20,7 +20,7 @@ end #-- # Qualified names are required to be relative because we are extending existing # methods that expect constant names, ie, relative paths of length 1. For example, -# Object.const_get("::String") raises NameError and so does qualified_const_get. +# Object.const_get('::String') raises NameError and so does qualified_const_get. #++ class Module def qualified_const_defined?(path, search_parents=true) diff --git a/activesupport/lib/active_support/core_ext/module/remove_method.rb b/activesupport/lib/active_support/core_ext/module/remove_method.rb index b76bc16ee1..719071d1c2 100644 --- a/activesupport/lib/active_support/core_ext/module/remove_method.rb +++ b/activesupport/lib/active_support/core_ext/module/remove_method.rb @@ -1,12 +1,8 @@ class Module def remove_possible_method(method) if method_defined?(method) || private_method_defined?(method) - remove_method(method) + undef_method(method) end - rescue NameError - # If the requested method is defined on a superclass or included module, - # method_defined? returns true but remove_method throws a NameError. - # Ignore this. end def redefine_method(method, &block) diff --git a/activesupport/lib/active_support/core_ext/numeric/time.rb b/activesupport/lib/active_support/core_ext/numeric/time.rb index 58a03d508e..822f766af7 100644 --- a/activesupport/lib/active_support/core_ext/numeric/time.rb +++ b/activesupport/lib/active_support/core_ext/numeric/time.rb @@ -28,9 +28,9 @@ class Numeric # 1.year.to_f.from_now # # In such cases, Ruby's core - # Date[http://stdlib.rubyonrails.org/libdoc/date/rdoc/index.html] and - # Time[http://stdlib.rubyonrails.org/libdoc/time/rdoc/index.html] should be used for precision - # date and time arithmetic + # Date[http://ruby-doc.org/stdlib/libdoc/date/rdoc/Date.html] and + # Time[http://ruby-doc.org/stdlib/libdoc/time/rdoc/Time.html] should be used for precision + # date and time arithmetic. def seconds ActiveSupport::Duration.new(self, [[:seconds, self]]) end diff --git a/activesupport/lib/active_support/core_ext/object/blank.rb b/activesupport/lib/active_support/core_ext/object/blank.rb index 7271671908..e238fef5a2 100644 --- a/activesupport/lib/active_support/core_ext/object/blank.rb +++ b/activesupport/lib/active_support/core_ext/object/blank.rb @@ -1,9 +1,8 @@ # encoding: utf-8 -require 'active_support/core_ext/string/encoding' class Object # An object is blank if it's false, empty, or a whitespace string. - # For example, "", " ", +nil+, [], and {} are all blank. + # For example, '', ' ', +nil+, [], and {} are all blank. # # This simplifies: # @@ -91,10 +90,10 @@ end class String # A string is blank if it's empty or contains whitespaces only: # - # "".blank? # => true - # " ".blank? # => true - # " ".blank? # => true - # " something here ".blank? # => false + # ''.blank? # => true + # ' '.blank? # => true + # ' '.blank? # => true + # ' something here '.blank? # => false # def blank? self !~ /[^[:space:]]/ diff --git a/activesupport/lib/active_support/core_ext/object/duplicable.rb b/activesupport/lib/active_support/core_ext/object/duplicable.rb index 9d044eba71..9d1630bb7c 100644 --- a/activesupport/lib/active_support/core_ext/object/duplicable.rb +++ b/activesupport/lib/active_support/core_ext/object/duplicable.rb @@ -104,3 +104,16 @@ class Module false end end + +require 'bigdecimal' +class BigDecimal + begin + BigDecimal.new('4.56').dup + + def duplicable? + true + end + rescue TypeError + # can't dup, so use superclass implementation + end +end diff --git a/activesupport/lib/active_support/core_ext/object/inclusion.rb b/activesupport/lib/active_support/core_ext/object/inclusion.rb index f611cdd606..3fec465ec0 100644 --- a/activesupport/lib/active_support/core_ext/object/inclusion.rb +++ b/activesupport/lib/active_support/core_ext/object/inclusion.rb @@ -2,11 +2,11 @@ class Object # Returns true if this object is included in the argument(s). Argument must be # any object which responds to +#include?+ or optionally, multiple arguments can be passed in. Usage: # - # characters = ["Konata", "Kagami", "Tsukasa"] - # "Konata".in?(characters) # => true - # - # character = "Konata" - # character.in?("Konata", "Kagami", "Tsukasa") # => true + # characters = ['Konata', 'Kagami', 'Tsukasa'] + # 'Konata'.in?(characters) # => true + # + # character = 'Konata' + # character.in?('Konata', 'Kagami', 'Tsukasa') # => true # # This will throw an ArgumentError if a single argument is passed in and it doesn't respond # to +#include?+. @@ -18,7 +18,7 @@ class Object if another_object.respond_to? :include? another_object.include? self else - raise ArgumentError.new("The single parameter passed to #in? must respond to #include?") + raise ArgumentError.new 'The single parameter passed to #in? must respond to #include?' end end end diff --git a/activesupport/lib/active_support/core_ext/object/instance_variables.rb b/activesupport/lib/active_support/core_ext/object/instance_variables.rb index 66caf9bec8..91fdf93eb2 100644 --- a/activesupport/lib/active_support/core_ext/object/instance_variables.rb +++ b/activesupport/lib/active_support/core_ext/object/instance_variables.rb @@ -10,7 +10,7 @@ class Object # # C.new(0, 1).instance_values # => {"x" => 0, "y" => 1} def instance_values #:nodoc: - Hash[instance_variables.map { |name| [name.to_s[1..-1], instance_variable_get(name)] }] + Hash[instance_variables.map { |name| [name[1..-1], instance_variable_get(name)] }] end # Returns an array of instance variable names including "@". They are strings diff --git a/activesupport/lib/active_support/core_ext/object/with_options.rb b/activesupport/lib/active_support/core_ext/object/with_options.rb index 1397142c04..e058367111 100644 --- a/activesupport/lib/active_support/core_ext/object/with_options.rb +++ b/activesupport/lib/active_support/core_ext/object/with_options.rb @@ -29,7 +29,7 @@ class Object # # It can also be used with an explicit receiver: # - # I18n.with_options :locale => user.locale, :scope => "newsletter" do |i18n| + # I18n.with_options :locale => user.locale, :scope => 'newsletter' do |i18n| # subject i18n.t :subject # body i18n.t :body, :user_name => user.name # end diff --git a/activesupport/lib/active_support/core_ext/proc.rb b/activesupport/lib/active_support/core_ext/proc.rb index 94bb5fb0cb..cd63740940 100644 --- a/activesupport/lib/active_support/core_ext/proc.rb +++ b/activesupport/lib/active_support/core_ext/proc.rb @@ -1,7 +1,10 @@ require "active_support/core_ext/kernel/singleton_class" +require "active_support/deprecation" class Proc #:nodoc: def bind(object) + ActiveSupport::Deprecation.warn 'Proc#bind is deprecated and will be removed in future versions', caller + block, time = self, Time.now object.class_eval do method_name = "__bind_#{time.to_i}_#{time.usec}" diff --git a/activesupport/lib/active_support/core_ext/range/include_range.rb b/activesupport/lib/active_support/core_ext/range/include_range.rb index 684b7cbc4a..3af66aaf2f 100644 --- a/activesupport/lib/active_support/core_ext/range/include_range.rb +++ b/activesupport/lib/active_support/core_ext/range/include_range.rb @@ -5,7 +5,7 @@ class Range # (1..5).include?(2..6) # => false # # The native Range#include? behavior is untouched. - # ("a".."f").include?("c") # => true + # ('a'..'f').include?('c') # => true # (5..9).include?(11) # => false def include_with_range?(value) if value.is_a?(::Range) 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.rb b/activesupport/lib/active_support/core_ext/string.rb index 72522d395c..ab49af55bf 100644 --- a/activesupport/lib/active_support/core_ext/string.rb +++ b/activesupport/lib/active_support/core_ext/string.rb @@ -9,6 +9,5 @@ require 'active_support/core_ext/string/behavior' require 'active_support/core_ext/string/interpolation' require 'active_support/core_ext/string/output_safety' require 'active_support/core_ext/string/exclude' -require 'active_support/core_ext/string/encoding' require 'active_support/core_ext/string/strip' require 'active_support/core_ext/string/inquiry' diff --git a/activesupport/lib/active_support/core_ext/string/access.rb b/activesupport/lib/active_support/core_ext/string/access.rb index 9b5266c58c..23aaee9c43 100644 --- a/activesupport/lib/active_support/core_ext/string/access.rb +++ b/activesupport/lib/active_support/core_ext/string/access.rb @@ -1,4 +1,4 @@ -require "active_support/multibyte" +require 'active_support/multibyte' class String def at(position) diff --git a/activesupport/lib/active_support/core_ext/string/conversions.rb b/activesupport/lib/active_support/core_ext/string/conversions.rb index 541f969faa..9084bbee32 100644 --- a/activesupport/lib/active_support/core_ext/string/conversions.rb +++ b/activesupport/lib/active_support/core_ext/string/conversions.rb @@ -4,21 +4,33 @@ require 'active_support/core_ext/time/calculations' class String # Form can be either :utc (default) or :local. def to_time(form = :utc) - return nil if self.blank? - d = ::Date._parse(self, false).values_at(:year, :mon, :mday, :hour, :min, :sec, :sec_fraction, :offset).map { |arg| arg || 0 } - d[6] *= 1000000 - ::Time.send("#{form}_time", *d[0..6]) - d[7] + unless blank? + date_values = ::Date._parse(self, false). + values_at(:year, :mon, :mday, :hour, :min, :sec, :sec_fraction, :offset). + map! { |arg| arg || 0 } + date_values[6] *= 1000000 + offset = date_values.pop + + ::Time.send("#{form}_time", *date_values) - offset + end end def to_date - return nil if self.blank? - ::Date.new(*::Date._parse(self, false).values_at(:year, :mon, :mday)) + unless blank? + date_values = ::Date._parse(self, false).values_at(:year, :mon, :mday) + + ::Date.new(*date_values) + end end def to_datetime - return nil if self.blank? - d = ::Date._parse(self, false).values_at(:year, :mon, :mday, :hour, :min, :sec, :zone, :sec_fraction).map { |arg| arg || 0 } - d[5] += d.pop - ::DateTime.civil(*d) + unless blank? + date_values = ::Date._parse(self, false). + values_at(:year, :mon, :mday, :hour, :min, :sec, :zone, :sec_fraction). + map! { |arg| arg || 0 } + date_values[5] += date_values.pop + + ::DateTime.civil(*date_values) + end end end diff --git a/activesupport/lib/active_support/core_ext/string/filters.rb b/activesupport/lib/active_support/core_ext/string/filters.rb index 1a34e88a87..32a37296d5 100644 --- a/activesupport/lib/active_support/core_ext/string/filters.rb +++ b/activesupport/lib/active_support/core_ext/string/filters.rb @@ -22,26 +22,33 @@ class String # Truncates a given +text+ after a given <tt>length</tt> if +text+ is longer than <tt>length</tt>: # - # "Once upon a time in a world far far away".truncate(27) + # 'Once upon a time in a world far far away'.truncate(27) # # => "Once upon a time in a wo..." # - # Pass a <tt>:separator</tt> to truncate +text+ at a natural break: + # Pass a string or regexp <tt>:separator</tt> to truncate +text+ at a natural break: # - # "Once upon a time in a world far far away".truncate(27, :separator => ' ') + # 'Once upon a time in a world far far away'.truncate(27, :separator => ' ') + # # => "Once upon a time in a..." + # + # 'Once upon a time in a world far far away'.truncate(27, :separator => /\s/) # # => "Once upon a time in a..." # # The last characters will be replaced with the <tt>:omission</tt> string (defaults to "...") # for a total length not exceeding <tt>:length</tt>: # - # "And they found that many people were sleeping better.".truncate(25, :omission => "... (continued)") + # 'And they found that many people were sleeping better.'.truncate(25, :omission => '... (continued)') # # => "And they f... (continued)" - def truncate(length, options = {}) - return self.dup unless self.length > length + def truncate(truncate_at, options = {}) + return dup unless length > truncate_at - options[:omission] ||= "..." - length_with_room_for_omission = length - options[:omission].length - stop = options[:separator] ? - (rindex(options[:separator], length_with_room_for_omission) || length_with_room_for_omission) : length_with_room_for_omission + options[:omission] ||= '...' + length_with_room_for_omission = truncate_at - options[:omission].length + stop = \ + if options[:separator] + rindex(options[:separator], length_with_room_for_omission) || length_with_room_for_omission + else + length_with_room_for_omission + end self[0...stop] + options[:omission] 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..049ffe7986 100644 --- a/activesupport/lib/active_support/core_ext/string/inflections.rb +++ b/activesupport/lib/active_support/core_ext/string/inflections.rb @@ -4,7 +4,7 @@ require 'active_support/inflector/transliterate' # String inflections define new methods on the String class to transform names for different purposes. # For instance, you can figure out the name of a table from the name of a class. # -# "ScaleScore".tableize # => "scale_scores" +# 'ScaleScore'.tableize # => "scale_scores" # class String # Returns the plural form of the word in the string. @@ -14,14 +14,14 @@ class String # For any other value of +count+ the plural will be returned. # # ==== Examples - # "post".pluralize # => "posts" - # "octopus".pluralize # => "octopi" - # "sheep".pluralize # => "sheep" - # "words".pluralize # => "words" - # "the blue mailman".pluralize # => "the blue mailmen" - # "CamelOctopus".pluralize # => "CamelOctopi" - # "apple".pluralize(1) # => "apple" - # "apple".pluralize(2) # => "apples" + # 'post'.pluralize # => "posts" + # 'octopus'.pluralize # => "octopi" + # 'sheep'.pluralize # => "sheep" + # 'words'.pluralize # => "words" + # 'the blue mailman'.pluralize # => "the blue mailmen" + # 'CamelOctopus'.pluralize # => "CamelOctopi" + # 'apple'.pluralize(1) # => "apple" + # 'apple'.pluralize(2) # => "apples" def pluralize(count = nil) if count == 1 self @@ -32,12 +32,12 @@ class String # The reverse of +pluralize+, returns the singular form of a word in a string. # - # "posts".singularize # => "post" - # "octopi".singularize # => "octopus" - # "sheep".singularize # => "sheep" - # "word".singularize # => "word" - # "the blue mailmen".singularize # => "the blue mailman" - # "CamelOctopi".singularize # => "CamelOctopus" + # 'posts'.singularize # => "post" + # 'octopi'.singularize # => "octopus" + # 'sheep'.singularize # => "sheep" + # 'word'.singularize # => "word" + # 'the blue mailmen'.singularize # => "the blue mailman" + # 'CamelOctopi'.singularize # => "CamelOctopus" def singularize ActiveSupport::Inflector.singularize(self) end @@ -47,9 +47,9 @@ class String # or is not initialized. See ActiveSupport::Inflector.constantize # # Examples - # "Module".constantize # => Module - # "Class".constantize # => Class - # "blargle".constantize # => NameError: wrong constant name blargle + # 'Module'.constantize # => Module + # 'Class'.constantize # => Class + # 'blargle'.constantize # => NameError: wrong constant name blargle def constantize ActiveSupport::Inflector.constantize(self) end @@ -59,9 +59,9 @@ class String # or is not initialized. See ActiveSupport::Inflector.safe_constantize # # Examples - # "Module".safe_constantize # => Module - # "Class".safe_constantize # => Class - # "blargle".safe_constantize # => nil + # 'Module'.safe_constantize # => Module + # 'Class'.safe_constantize # => Class + # 'blargle'.safe_constantize # => nil def safe_constantize ActiveSupport::Inflector.safe_constantize(self) end @@ -71,14 +71,16 @@ class String # # +camelize+ will also convert '/' to '::' which is useful for converting paths to namespaces. # - # "active_record".camelize # => "ActiveRecord" - # "active_record".camelize(:lower) # => "activeRecord" - # "active_record/errors".camelize # => "ActiveRecord::Errors" - # "active_record/errors".camelize(:lower) # => "activeRecord::Errors" + # 'active_record'.camelize # => "ActiveRecord" + # 'active_record'.camelize(:lower) # => "activeRecord" + # 'active_record/errors'.camelize # => "ActiveRecord::Errors" + # 'active_record/errors'.camelize(:lower) # => "activeRecord::Errors" def camelize(first_letter = :upper) case first_letter - when :upper then ActiveSupport::Inflector.camelize(self, true) - when :lower then ActiveSupport::Inflector.camelize(self, false) + when :upper + ActiveSupport::Inflector.camelize(self, true) + when :lower + ActiveSupport::Inflector.camelize(self, false) end end alias_method :camelcase, :camelize @@ -89,8 +91,8 @@ class String # # +titleize+ is also aliased as +titlecase+. # - # "man from the boondocks".titleize # => "Man From The Boondocks" - # "x-men: the last stand".titleize # => "X Men: The Last Stand" + # 'man from the boondocks'.titleize # => "Man From The Boondocks" + # 'x-men: the last stand'.titleize # => "X Men: The Last Stand" def titleize ActiveSupport::Inflector.titleize(self) end @@ -100,23 +102,23 @@ 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 # Replaces underscores with dashes in the string. # - # "puni_puni" # => "puni-puni" + # 'puni_puni' # => "puni-puni" def dasherize ActiveSupport::Inflector.dasherize(self) end # Removes the module part from the constant expression in the string. # - # "ActiveRecord::CoreExtensions::String::Inflections".demodulize # => "Inflections" - # "Inflections".demodulize # => "Inflections" + # 'ActiveRecord::CoreExtensions::String::Inflections'.demodulize # => "Inflections" + # 'Inflections'.demodulize # => "Inflections" # # See also +deconstantize+. def demodulize @@ -125,11 +127,11 @@ class String # Removes the rightmost segment from the constant expression in the string. # - # "Net::HTTP".deconstantize # => "Net" - # "::Net::HTTP".deconstantize # => "::Net" - # "String".deconstantize # => "" - # "::String".deconstantize # => "" - # "".deconstantize # => "" + # 'Net::HTTP'.deconstantize # => "Net" + # '::Net::HTTP'.deconstantize # => "::Net" + # 'String'.deconstantize # => "" + # '::String'.deconstantize # => "" + # ''.deconstantize # => "" # # See also +demodulize+. def deconstantize @@ -158,9 +160,9 @@ class String # Creates the name of a table like Rails does for models to table names. This method # uses the +pluralize+ method on the last word in the string. # - # "RawScaledScorer".tableize # => "raw_scaled_scorers" - # "egg_and_ham".tableize # => "egg_and_hams" - # "fancyCategory".tableize # => "fancy_categories" + # 'RawScaledScorer'.tableize # => "raw_scaled_scorers" + # 'egg_and_ham'.tableize # => "egg_and_hams" + # 'fancyCategory'.tableize # => "fancy_categories" def tableize ActiveSupport::Inflector.tableize(self) end @@ -169,12 +171,12 @@ class String # Note that this returns a string and not a class. (To convert to an actual class # follow +classify+ with +constantize+.) # - # "egg_and_hams".classify # => "EggAndHam" - # "posts".classify # => "Post" + # 'egg_and_hams'.classify # => "EggAndHam" + # 'posts'.classify # => "Post" # # Singular names are not handled correctly. # - # "business".classify # => "Busines" + # 'business'.classify # => "Busines" def classify ActiveSupport::Inflector.classify(self) end @@ -182,8 +184,8 @@ class String # Capitalizes the first word, turns underscores into spaces, and strips '_id'. # Like +titleize+, this is meant for creating pretty output. # - # "employee_salary" # => "Employee salary" - # "author_id" # => "Author" + # 'employee_salary' # => "Employee salary" + # 'author_id' # => "Author" def humanize ActiveSupport::Inflector.humanize(self) end @@ -193,9 +195,9 @@ class String # the method should put '_' between the name and 'id'. # # Examples - # "Message".foreign_key # => "message_id" - # "Message".foreign_key(false) # => "messageid" - # "Admin::Post".foreign_key # => "post_id" + # 'Message'.foreign_key # => "message_id" + # 'Message'.foreign_key(false) # => "messageid" + # 'Admin::Post'.foreign_key # => "post_id" def foreign_key(separate_class_name_and_id_with_underscore = true) ActiveSupport::Inflector.foreign_key(self, separate_class_name_and_id_with_underscore) end diff --git a/activesupport/lib/active_support/core_ext/string/inquiry.rb b/activesupport/lib/active_support/core_ext/string/inquiry.rb index 5f0a017de6..2562a7cef6 100644 --- a/activesupport/lib/active_support/core_ext/string/inquiry.rb +++ b/activesupport/lib/active_support/core_ext/string/inquiry.rb @@ -4,7 +4,7 @@ class String # Wraps the current string in the <tt>ActiveSupport::StringInquirer</tt> class, # which gives you a prettier way to test for equality. Example: # - # env = "production".inquiry + # env = 'production'.inquiry # env.production? # => true # env.development? # => false def inquiry 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..215ba87ca9 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>. @@ -13,7 +15,7 @@ class ERB # <%=h @person.name %> # # ==== Example: - # puts html_escape("is a > 0 & a < 10?") + # puts html_escape('is a > 0 & a < 10?') # # => is a > 0 & a < 10? def html_escape(s) s = s.to_s @@ -33,10 +35,25 @@ 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: # - # json_escape("is a > 0 & a < 10?") + # json_escape('is a > 0 & a < 10?') # # => is a \u003E 0 \u0026 a \u003C 10? # # Note that after this operation is performed the output is not @@ -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 @@ -75,40 +92,55 @@ end module ActiveSupport #:nodoc: class SafeBuffer < String - UNSAFE_STRING_METHODS = ["capitalize", "chomp", "chop", "delete", "downcase", "gsub", "lstrip", "next", "reverse", "rstrip", "slice", "squeeze", "strip", "sub", "succ", "swapcase", "tr", "tr_s", "upcase", "prepend"].freeze + UNSAFE_STRING_METHODS = %w( + capitalize chomp chop delete downcase gsub lstrip next reverse rstrip + slice squeeze strip sub succ swapcase tr tr_s upcase prepend + ) alias_method :original_concat, :concat private :original_concat class SafeConcatError < StandardError def initialize - super "Could not concatenate to the buffer because it is not html safe." + super 'Could not concatenate to the buffer because it is not html safe.' end end - def[](*args) - new_safe_buffer = super - new_safe_buffer.instance_eval { @dirty = false } - new_safe_buffer + def [](*args) + if args.size < 2 + super + else + if html_safe? + new_safe_buffer = super + new_safe_buffer.instance_eval { @html_safe = true } + new_safe_buffer + else + to_str[*args] + end + end end def safe_concat(value) - raise SafeConcatError if dirty? + raise SafeConcatError unless html_safe? original_concat(value) end def initialize(*) - @dirty = false + @html_safe = true super end def initialize_copy(other) super - @dirty = other.dirty? + @html_safe = other.html_safe? + end + + def clone_empty + self[0, 0] end def concat(value) - if dirty? || value.html_safe? + if !html_safe? || value.html_safe? super(value) else super(ERB::Util.h(value)) @@ -121,7 +153,7 @@ module ActiveSupport #:nodoc: end def html_safe? - !dirty? + defined?(@html_safe) && @html_safe end def to_s @@ -144,18 +176,12 @@ module ActiveSupport #:nodoc: end # end def #{unsafe_method}!(*args) # def capitalize!(*args) - @dirty = true # @dirty = true + @html_safe = false # @html_safe = false super # super end # end EOT end end - - protected - - def dirty? - @dirty - end end 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..1434e186c3 100644 --- a/activesupport/lib/active_support/core_ext/time/calculations.rb +++ b/activesupport/lib/active_support/core_ext/time/calculations.rb @@ -1,10 +1,17 @@ require 'active_support/duration' -require 'active_support/core_ext/time/zones' require 'active_support/core_ext/time/conversions' class Time COMMON_YEAR_DAYS_IN_MONTH = [nil, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31] - DAYS_INTO_WEEK = { :monday => 0, :tuesday => 1, :wednesday => 2, :thursday => 3, :friday => 4, :saturday => 5, :sunday => 6 } + DAYS_INTO_WEEK = { + :monday => 0, + :tuesday => 1, + :wednesday => 2, + :thursday => 3, + :friday => 4, + :saturday => 5, + :sunday => 6 + } class << self # Overriding case equality method so that it returns true for ActiveSupport::TimeWithZone instances @@ -15,8 +22,11 @@ class Time # Return the number of days in the given month. # If no year is specified, it will use the current year. def days_in_month(month, year = now.year) - return 29 if month == 2 && ::Date.gregorian_leap?(year) - COMMON_YEAR_DAYS_IN_MONTH[month] + if month == 2 && ::Date.gregorian_leap?(year) + 29 + else + COMMON_YEAR_DAYS_IN_MONTH[month] + end end # Returns a new Time if requested year can be accommodated by Ruby's Time class @@ -24,8 +34,13 @@ class Time # otherwise returns a DateTime. def time_with_datetime_fallback(utc_or_local, year, month=1, day=1, hour=0, min=0, sec=0, usec=0) time = ::Time.send(utc_or_local, year, month, day, hour, min, sec, usec) + # This check is needed because Time.utc(y) returns a time object in the 2000s for 0 <= y <= 138. - time.year == year ? time : ::DateTime.civil_from_format(utc_or_local, year, month, day, hour, min, sec) + if time.year == year + time + else + ::DateTime.civil_from_format(utc_or_local, year, month, day, hour, min, sec) + end rescue ::DateTime.civil_from_format(utc_or_local, year, month, day, hour, min, sec) end @@ -67,18 +82,18 @@ class Time end # Returns a new Time where one or more of the elements have been changed according to the +options+ parameter. The time options - # (hour, minute, sec, usec) reset cascadingly, so if only the hour is passed, then minute, sec, and usec is set to 0. If the hour and + # (hour, min, sec, usec) reset cascadingly, so if only the hour is passed, then minute, sec, and usec is set to 0. If the hour and # minute is passed, then sec and usec is set to 0. def change(options) ::Time.send( utc? ? :utc_time : :local_time, - options[:year] || year, - options[:month] || month, - options[:day] || day, - options[:hour] || hour, - options[:min] || (options[:hour] ? 0 : min), - options[:sec] || ((options[:hour] || options[:min]) ? 0 : sec), - options[:usec] || ((options[:hour] || options[:min] || options[:sec]) ? 0 : Rational(nsec, 1000)) + options.fetch(:year, year), + options.fetch(:month, month), + options.fetch(:day, day), + options.fetch(:hour, hour), + options.fetch(:min, options[:hour] ? 0 : min), + options.fetch(:sec, (options[:hour] || options[:min]) ? 0 : sec), + options.fetch(:usec, (options[:hour] || options[:min] || options[:sec]) ? 0 : Rational(nsec, 1000)) ) end @@ -89,18 +104,26 @@ class Time def advance(options) unless options[:weeks].nil? options[:weeks], partial_weeks = options[:weeks].divmod(1) - options[:days] = (options[:days] || 0) + 7 * partial_weeks + options[:days] = options.fetch(:days, 0) + 7 * partial_weeks end unless options[:days].nil? options[:days], partial_days = options[:days].divmod(1) - options[:hours] = (options[:hours] || 0) + 24 * partial_days + options[:hours] = options.fetch(:hours, 0) + 24 * partial_days end d = to_date.advance(options) time_advanced_by_date = change(:year => d.year, :month => d.month, :day => d.day) - seconds_to_advance = (options[:seconds] || 0) + (options[:minutes] || 0) * 60 + (options[:hours] || 0) * 3600 - seconds_to_advance == 0 ? time_advanced_by_date : time_advanced_by_date.since(seconds_to_advance) + seconds_to_advance = \ + options.fetch(:seconds, 0) + + options.fetch(:minutes, 0) * 60 + + options.fetch(:hours, 0) * 3600 + + if seconds_to_advance.zero? + time_advanced_by_date + else + time_advanced_by_date.since(seconds_to_advance) + end end # Returns a new Time representing the time a number of seconds ago, this is basically a wrapper around the Numeric extension @@ -145,6 +168,7 @@ class Time def prev_year years_ago(1) end + alias_method :last_year, :prev_year # Short-hand for years_since(1) def next_year @@ -155,6 +179,7 @@ class Time def prev_month months_ago(1) end + alias_method :last_month, :prev_month # Short-hand for months_since(1) def next_month @@ -166,6 +191,7 @@ class Time start_day_number = DAYS_INTO_WEEK[start_day] current_day_number = wday != 0 ? wday - 1 : 6 days_span = current_day_number - start_day_number + days_span >= 0 ? days_span : 7 + days_span end @@ -197,12 +223,19 @@ class Time # Returns a new Time representing the start of the given day in the previous week (default is :monday). def prev_week(day = :monday) - ago(1.week).beginning_of_week.since(DAYS_INTO_WEEK[day].day).change(:hour => 0) + ago(1.week). + beginning_of_week. + since(DAYS_INTO_WEEK[day].day). + change(:hour => 0) end + alias_method :last_week, :prev_week # Returns a new Time representing the start of the given day in next week (default is :monday). def next_week(day = :monday) - since(1.week).beginning_of_week.since(DAYS_INTO_WEEK[day].day).change(:hour => 0) + since(1.week). + beginning_of_week. + since(DAYS_INTO_WEEK[day].day). + change(:hour => 0) end # Returns a new Time representing the start of the day (0:00) @@ -216,7 +249,12 @@ class Time # Returns a new Time representing the end of the day, 23:59:59.999999 (.999999999 in ruby1.9) def end_of_day - change(:hour => 23, :min => 59, :sec => 59, :usec => 999999.999) + change( + :hour => 23, + :min => 59, + :sec => 59, + :usec => 999999.999 + ) end # Returns a new Time representing the start of the month (1st of the month, 0:00) @@ -230,19 +268,27 @@ class Time def end_of_month #self - ((self.mday-1).days + self.seconds_since_midnight) last_day = ::Time.days_in_month(month, year) - change(:day => last_day, :hour => 23, :min => 59, :sec => 59, :usec => 999999.999) + change( + :day => last_day, + :hour => 23, + :min => 59, + :sec => 59, + :usec => 999999.999 + ) end alias :at_end_of_month :end_of_month # Returns a new Time representing the start of the quarter (1st of january, april, july, october, 0:00) def beginning_of_quarter - beginning_of_month.change(:month => [10, 7, 4, 1].detect { |m| m <= month }) + first_quarter_month = [10, 7, 4, 1].detect { |m| m <= month } + beginning_of_month.change(:month => first_quarter_month) end alias :at_beginning_of_quarter :beginning_of_quarter # Returns a new Time representing the end of the quarter (end of the last day of march, june, september, december) def end_of_quarter - beginning_of_month.change(:month => [3, 6, 9, 12].detect { |m| m >= month }).end_of_month + last_quarter_month = [3, 6, 9, 12].detect { |m| m >= month } + beginning_of_month.change(:month => last_quarter_month).end_of_month end alias :at_end_of_quarter :end_of_quarter @@ -254,7 +300,14 @@ class Time # Returns a new Time representing the end of the year (end of the 31st of december) def end_of_year - change(:month => 12, :day => 31, :hour => 23, :min => 59, :sec => 59, :usec => 999999.999) + change( + :month => 12, + :day => 31, + :hour => 23, + :min => 59, + :sec => 59, + :usec => 999999.999 + ) end alias :at_end_of_year :end_of_year @@ -273,9 +326,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. @@ -327,7 +380,11 @@ class Time # can be chronologically compared with a Time def compare_with_coercion(other) # we're avoiding Time#to_datetime cause it's expensive - other.is_a?(Time) ? compare_without_coercion(other.to_time) : to_datetime <=> other + if other.is_a?(Time) + compare_without_coercion(other.to_time) + else + to_datetime <=> other + end end alias_method :compare_without_coercion, :<=> alias_method :<=>, :compare_with_coercion @@ -341,4 +398,5 @@ class Time end alias_method :eql_without_coercion, :eql? alias_method :eql?, :eql_with_coercion + end diff --git a/activesupport/lib/active_support/core_ext/time/conversions.rb b/activesupport/lib/active_support/core_ext/time/conversions.rb index 5a17fc1afa..4f852fd780 100644 --- a/activesupport/lib/active_support/core_ext/time/conversions.rb +++ b/activesupport/lib/active_support/core_ext/time/conversions.rb @@ -3,13 +3,19 @@ require 'active_support/values/time_zone' class Time DATE_FORMATS = { - :db => "%Y-%m-%d %H:%M:%S", - :number => "%Y%m%d%H%M%S", - :time => "%H:%M", - :short => "%d %b %H:%M", - :long => "%B %d, %Y %H:%M", - :long_ordinal => lambda { |time| time.strftime("%B #{ActiveSupport::Inflector.ordinalize(time.day)}, %Y %H:%M") }, - :rfc822 => lambda { |time| time.strftime("%a, %d %b %Y %H:%M:%S #{time.formatted_offset(false)}") } + :db => '%Y-%m-%d %H:%M:%S', + :number => '%Y%m%d%H%M%S', + :time => '%H:%M', + :short => '%d %b %H:%M', + :long => '%B %d, %Y %H:%M', + :long_ordinal => lambda { |time| + day_format = ActiveSupport::Inflector.ordinalize(time.day) + time.strftime("%B #{day_format}, %Y %H:%M") + }, + :rfc822 => lambda { |time| + offset_format = time.formatted_offset(false) + time.strftime("%a, %d %b %Y %H:%M:%S #{offset_format}") + } } # Converts to a formatted string. See DATE_FORMATS for builtin formats. @@ -34,7 +40,7 @@ class Time # or Proc instance that takes a time argument as the value. # # # config/initializers/time_formats.rb - # Time::DATE_FORMATS[:month_and_year] = "%B %Y" + # Time::DATE_FORMATS[:month_and_year] = '%B %Y' # Time::DATE_FORMATS[:short_ordinal] = lambda { |time| time.strftime("%B #{time.day.ordinalize}") } def to_formatted_s(format = :default) if formatter = DATE_FORMATS[format] @@ -53,32 +59,4 @@ class Time def formatted_offset(colon = true, alternate_utc_string = nil) utc? && alternate_utc_string || ActiveSupport::TimeZone.seconds_to_utc_offset(utc_offset, colon) end - - # Converts a Time object to a Date, dropping hour, minute, and second precision. - # - # my_time = Time.now # => Mon Nov 12 22:59:51 -0500 2007 - # my_time.to_date # => Mon, 12 Nov 2007 - # - # your_time = Time.parse("1/13/2009 1:13:03 P.M.") # => Tue Jan 13 13:13:03 -0500 2009 - # your_time.to_date # => Tue, 13 Jan 2009 - def to_date - ::Date.new(year, month, day) - end unless method_defined?(:to_date) - - # A method to keep Time, Date and DateTime instances interchangeable on conversions. - # In this case, it simply returns +self+. - def to_time - self - end unless method_defined?(:to_time) - - # Converts a Time instance to a Ruby DateTime instance, preserving UTC offset. - # - # my_time = Time.now # => Mon Nov 12 23:04:21 -0500 2007 - # my_time.to_datetime # => Mon, 12 Nov 2007 23:04:21 -0500 - # - # your_time = Time.parse("1/13/2009 1:13:03 P.M.") # => Tue Jan 13 13:13:03 -0500 2009 - # your_time.to_datetime # => Tue, 13 Jan 2009 13:13:03 -0500 - def to_datetime - ::DateTime.civil(year, month, day, hour, min, sec, Rational(utc_offset, 86400)) - end unless method_defined?(:to_datetime) end diff --git a/activesupport/lib/active_support/core_ext/time/zones.rb b/activesupport/lib/active_support/core_ext/time/zones.rb index 0c5962858e..e48866abe3 100644 --- a/activesupport/lib/active_support/core_ext/time/zones.rb +++ b/activesupport/lib/active_support/core_ext/time/zones.rb @@ -1,3 +1,4 @@ +require 'active_support/core_ext/time/calculations' require 'active_support/time_with_zone' class Time @@ -50,13 +51,21 @@ class Time # Returns a TimeZone instance or nil, or raises an ArgumentError for invalid timezones. def find_zone!(time_zone) - return time_zone if time_zone.nil? || time_zone.is_a?(ActiveSupport::TimeZone) - # lookup timezone based on identifier (unless we've been passed a TZInfo::Timezone) - unless time_zone.respond_to?(:period_for_local) - time_zone = ActiveSupport::TimeZone[time_zone] || TZInfo::Timezone.get(time_zone) + if !time_zone || time_zone.is_a?(ActiveSupport::TimeZone) + time_zone + else + # lookup timezone based on identifier (unless we've been passed a TZInfo::Timezone) + unless time_zone.respond_to?(:period_for_local) + time_zone = ActiveSupport::TimeZone[time_zone] || TZInfo::Timezone.get(time_zone) + end + + # Return if a TimeZone instance, or wrap in a TimeZone instance if a TZInfo::Timezone + if time_zone.is_a?(ActiveSupport::TimeZone) + time_zone + else + ActiveSupport::TimeZone.create(time_zone.name, nil, time_zone) + end end - # Return if a TimeZone instance, or wrap in a TimeZone instance if a TZInfo::Timezone - time_zone.is_a?(ActiveSupport::TimeZone) ? time_zone : ActiveSupport::TimeZone.create(time_zone.name, nil, time_zone) rescue TZInfo::InvalidTimezoneIdentifier raise ArgumentError, "Invalid Timezone: #{time_zone}" end @@ -79,8 +88,10 @@ class Time # # Time.utc(2000).in_time_zone('Alaska') # => Fri, 31 Dec 1999 15:00:00 AKST -09:00 def in_time_zone(zone = ::Time.zone) - return self unless zone - - ActiveSupport::TimeWithZone.new(utc? ? self : getutc, ::Time.find_zone!(zone)) + if zone + ActiveSupport::TimeWithZone.new(utc? ? self : getutc, ::Time.find_zone!(zone)) + else + self + end end end 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/dependencies.rb b/activesupport/lib/active_support/dependencies.rb index e121e452a3..745a131524 100644 --- a/activesupport/lib/active_support/dependencies.rb +++ b/activesupport/lib/active_support/dependencies.rb @@ -105,7 +105,7 @@ module ActiveSupport #:nodoc: next unless mod.is_a?(Module) # Get a list of the constants that were added - new_constants = mod.local_constant_names - original_constants + new_constants = mod.local_constants - original_constants # self[namespace] returns an Array of the constants that are being evaluated # for that namespace. For instance, if parent.rb requires child.rb, the first @@ -129,16 +129,14 @@ module ActiveSupport #:nodoc: # Add a set of modules to the watch stack, remembering the initial constants def watch_namespaces(namespaces) - watching = [] - namespaces.map do |namespace| + @watching << namespaces.map do |namespace| module_name = Dependencies.to_constant_name(namespace) original_constants = Dependencies.qualified_const_defined?(module_name) ? - Inflector.constantize(module_name).local_constant_names : [] + Inflector.constantize(module_name).local_constants : [] - watching << module_name @stack[module_name] << original_constants + module_name end - @watching << watching end private @@ -365,7 +363,7 @@ module ActiveSupport #:nodoc: # Record history *after* loading so first load gets warnings. history << expanded - return result + result end # Is the provided constant path defined? @@ -434,7 +432,7 @@ module ActiveSupport #:nodoc: mod = Module.new into.const_set const_name, mod autoloaded_constants << qualified_name unless autoload_once_paths.include?(base_path) - return mod + mod end # Load the file at the provided path. +const_paths+ is a set of qualified @@ -458,7 +456,7 @@ module ActiveSupport #:nodoc: autoloaded_constants.concat newly_defined_paths unless load_once_path?(path) autoloaded_constants.uniq! log "loading #{path} defined #{newly_defined_paths * ', '}" unless newly_defined_paths.empty? - return result + result end # Return the constant path for the provided parent and constant name. @@ -505,7 +503,7 @@ module ActiveSupport #:nodoc: raise NameError, "uninitialized constant #{qualified_name}", - caller.reject {|l| l.starts_with? __FILE__ } + caller.reject { |l| l.starts_with? __FILE__ } end # Remove the constants that have been autoloaded, and those that have been @@ -543,10 +541,7 @@ module ActiveSupport #:nodoc: def safe_get(key) key = key.name if key.respond_to?(:name) - @store[key] || begin - klass = Inflector.safe_constantize(key) - @store[key] = klass - end + @store[key] ||= Inflector.safe_constantize(key) end def store(klass) @@ -600,10 +595,10 @@ module ActiveSupport #:nodoc: def mark_for_unload(const_desc) name = to_constant_name const_desc if explicitly_unloadable_constants.include? name - return false + false else explicitly_unloadable_constants << name - return true + true end end @@ -631,10 +626,10 @@ module ActiveSupport #:nodoc: return new_constants unless aborting log "Error during loading, removing partially loaded constants " - new_constants.each {|c| remove_constant(c) }.clear + new_constants.each { |c| remove_constant(c) }.clear end - return [] + [] end # Convert the provided const desc to a qualified constant name (as a string). @@ -663,7 +658,7 @@ module ActiveSupport #:nodoc: constantized.before_remove_const if constantized.respond_to?(:before_remove_const) parent.instance_eval { remove_const to_remove } - return true + true end protected diff --git a/activesupport/lib/active_support/dependencies/autoload.rb b/activesupport/lib/active_support/dependencies/autoload.rb index 4c771da096..a1626ebeba 100644 --- a/activesupport/lib/active_support/dependencies/autoload.rb +++ b/activesupport/lib/active_support/dependencies/autoload.rb @@ -9,13 +9,16 @@ module ActiveSupport @@eager_autoload = false def autoload(const_name, path = @@at_path) - full = [self.name, @@under_path, const_name.to_s, path].compact.join("::") - location = path || Inflector.underscore(full) + unless path + full = [name, @@under_path, const_name.to_s, path].compact.join("::") + path = Inflector.underscore(full) + end if @@eager_autoload - @@autoloads[const_name] = location + @@autoloads[const_name] = path end - super const_name, location + + super const_name, path end def autoload_under(path) diff --git a/activesupport/lib/active_support/deprecation.rb b/activesupport/lib/active_support/deprecation.rb index 45b9dda5ca..176edefa42 100644 --- a/activesupport/lib/active_support/deprecation.rb +++ b/activesupport/lib/active_support/deprecation.rb @@ -1,3 +1,4 @@ +require 'active_support/core_ext/module/deprecation' require 'active_support/deprecation/behaviors' require 'active_support/deprecation/reporting' require 'active_support/deprecation/method_wrappers' diff --git a/activesupport/lib/active_support/deprecation/behaviors.rb b/activesupport/lib/active_support/deprecation/behaviors.rb index 94f8d7133e..9102537810 100644 --- a/activesupport/lib/active_support/deprecation/behaviors.rb +++ b/activesupport/lib/active_support/deprecation/behaviors.rb @@ -6,17 +6,31 @@ module ActiveSupport # Whether to print a backtrace along with the warning. attr_accessor :debug - # Returns the set behavior or if one isn't set, defaults to +:stderr+ + # Returns the current behavior or if one isn't set, defaults to +:stderr+ def behavior @behavior ||= [DEFAULT_BEHAVIORS[:stderr]] end - # Sets the behavior to the specified value. Can be a single value or an array. + # Sets the behavior to the specified value. Can be a single value, array, or + # an object that responds to +call+. # - # Examples + # Available behaviors: + # + # [+stderr+] Log all deprecation warnings to +$stderr+. + # [+log+] Log all deprecation warnings to +Rails.logger+. + # [+notify] Use +ActiveSupport::Notifications+ to notify +deprecation.rails+. + # [+silence+] Do nothing. + # + # Setting behaviors only affects deprecations that happen after boot time. + # Deprecation warnings raised by gems are not affected by this setting because + # they happen before Rails boots up. # # ActiveSupport::Deprecation.behavior = :stderr # ActiveSupport::Deprecation.behavior = [:stderr, :log] + # ActiveSupport::Deprecation.behavior = MyCustomHandler + # ActiveSupport::Deprecation.behavior = proc { |message, callstack| + # # custom stuff + # } def behavior=(behavior) @behavior = Array(behavior).map { |b| DEFAULT_BEHAVIORS[b] || b } end @@ -41,8 +55,9 @@ module ActiveSupport }, :notify => Proc.new { |message, callstack| ActiveSupport::Notifications.instrument("deprecation.rails", - :message => message, :callstack => callstack) - } + :message => message, :callstack => callstack) + }, + :silence => Proc.new { |message, callstack| } } end end diff --git a/activesupport/lib/active_support/deprecation/method_wrappers.rb b/activesupport/lib/active_support/deprecation/method_wrappers.rb index d0d8b577b3..c5de5e6a95 100644 --- a/activesupport/lib/active_support/deprecation/method_wrappers.rb +++ b/activesupport/lib/active_support/deprecation/method_wrappers.rb @@ -1,11 +1,10 @@ -require 'active_support/core_ext/module/deprecation' require 'active_support/core_ext/module/aliasing' require 'active_support/core_ext/array/extract_options' module ActiveSupport - class << Deprecation + module Deprecation # Declare that a method has been deprecated. - def deprecate_methods(target_module, *method_names) + def self.deprecate_methods(target_module, *method_names) options = method_names.extract_options! method_names += options.keys diff --git a/activesupport/lib/active_support/file_update_checker.rb b/activesupport/lib/active_support/file_update_checker.rb index 2ede084e95..8860636168 100644 --- a/activesupport/lib/active_support/file_update_checker.rb +++ b/activesupport/lib/active_support/file_update_checker.rb @@ -1,5 +1,3 @@ -require "active_support/core_ext/array/extract_options" - module ActiveSupport # \FileUpdateChecker specifies the API used by Rails to watch files # and control reloading. The API depends on four methods: @@ -11,7 +9,7 @@ module ActiveSupport # the filesystem or not; # # * +execute+ which executes the given block on initialization - # and updates the counter to the latest timestamp; + # and updates the latest watched files and timestamp; # # * +execute_if_updated+ which just executes the block if it was updated; # @@ -41,13 +39,13 @@ module ActiveSupport # # == Implementation details # - # This particular implementation checks for added and updated files, - # but not removed files. Directories lookup are compiled to a glob for - # performance. Therefore, while someone can add new files to the +files+ - # array after initialization (and parts of Rails do depend on this feature), - # adding new directories after initialization is not allowed. + # This particular implementation checks for added, updated, and removed + # files. Directories lookup are compiled to a glob for performance. + # Therefore, while someone can add new files to the +files+ array after + # initialization (and parts of Rails do depend on this feature), adding + # new directories after initialization is not supported. # - # Notice that other objects that implements FileUpdateChecker API may + # Notice that other objects that implement the FileUpdateChecker API may # not even allow new files to be added after initialization. If this # is the case, we recommend freezing the +files+ after initialization to # avoid changes that won't make effect. @@ -55,27 +53,41 @@ module ActiveSupport @files = files @glob = compile_glob(dirs) @block = block + + @watched = nil @updated_at = nil - @last_update_at = updated_at + + @last_watched = watched + @last_update_at = updated_at(@last_watched) end - # Check if any of the entries were updated. If so, the updated_at - # value is cached until the block is executed via +execute+ or +execute_if_updated+ + # Check if any of the entries were updated. If so, the watched and/or + # updated_at values are cached until the block is executed via +execute+ + # or +execute_if_updated+ def updated? - current_updated_at = updated_at - if @last_update_at < current_updated_at - @updated_at = updated_at + current_watched = watched + if @last_watched.size != current_watched.size + @watched = current_watched true else - false + current_updated_at = updated_at(current_watched) + if @last_update_at < current_updated_at + @watched = current_watched + @updated_at = current_updated_at + true + else + false + end end end - # Executes the given block and updates the counter to latest timestamp. + # Executes the given block and updates the latest watched files and timestamp. def execute - @last_update_at = updated_at + @last_watched = watched + @last_update_at = updated_at(@last_watched) @block.call ensure + @watched = nil @updated_at = nil end @@ -91,27 +103,33 @@ module ActiveSupport private - def updated_at #:nodoc: - @updated_at || begin - all = [] - all.concat @files.select { |f| File.exists?(f) } - all.concat Dir[@glob] if @glob - all.map { |path| File.mtime(path) }.max || Time.at(0) + def watched + @watched || begin + all = @files.select { |f| File.exists?(f) } + all.concat(Dir[@glob]) if @glob + all end end - def compile_glob(hash) #:nodoc: + def updated_at(paths) + @updated_at || paths.map { |path| File.mtime(path) }.max || Time.at(0) + end + + def compile_glob(hash) hash.freeze # Freeze so changes aren't accidently pushed return if hash.empty? - globs = [] - hash.each do |key, value| - globs << "#{key}/**/*#{compile_ext(value)}" + globs = hash.map do |key, value| + "#{escape(key)}/**/*#{compile_ext(value)}" end "{#{globs.join(",")}}" end - def compile_ext(array) #:nodoc: + def escape(key) + key.gsub(',','\,') + end + + def compile_ext(array) array = Array(array) return if array.empty? ".{#{array.join(",")}}" diff --git a/activesupport/lib/active_support/gzip.rb b/activesupport/lib/active_support/gzip.rb index f7036315d6..420b965c87 100644 --- a/activesupport/lib/active_support/gzip.rb +++ b/activesupport/lib/active_support/gzip.rb @@ -1,6 +1,5 @@ require 'zlib' require 'stringio' -require 'active_support/core_ext/string/encoding' module ActiveSupport # A convenient wrapper for the zlib standard library that allows compression/decompression of strings with gzip. diff --git a/activesupport/lib/active_support/hash_with_indifferent_access.rb b/activesupport/lib/active_support/hash_with_indifferent_access.rb index 674e4acfd6..91459f3e5b 100644 --- a/activesupport/lib/active_support/hash_with_indifferent_access.rb +++ b/activesupport/lib/active_support/hash_with_indifferent_access.rb @@ -1,12 +1,11 @@ require 'active_support/core_ext/hash/keys' -# This class has dubious semantics and we only have it so that -# people can write <tt>params[:key]</tt> instead of <tt>params['key']</tt> -# and they get the same value for both keys. - module ActiveSupport + # This class has dubious semantics and we only have it so that + # people can write <tt>params[:key]</tt> instead of <tt>params['key']</tt> + # and they get the same value for both keys. class HashWithIndifferentAccess < Hash - + # Always returns true, so that <tt>Array#extract_options!</tt> finds members of this class. def extractable_options? true @@ -43,6 +42,10 @@ module ActiveSupport end end + def self.[](*args) + new.merge(Hash[*args]) + end + alias_method :regular_writer, :[]= unless method_defined?(:regular_writer) alias_method :regular_update, :update unless method_defined?(:regular_update) diff --git a/activesupport/lib/active_support/inflections.rb b/activesupport/lib/active_support/inflections.rb index 527cce2594..7eb61cd1a0 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') @@ -16,17 +16,18 @@ module ActiveSupport inflect.plural(/([^aeiouy]|qu)y$/i, '\1ies') inflect.plural(/(x|ch|ss|sh)$/i, '\1es') inflect.plural(/(matr|vert|ind)(?:ix|ex)$/i, '\1ices') - inflect.plural(/(m|l)ouse$/i, '\1ice') - inflect.plural(/(m|l)ice$/i, '\1ice') + inflect.plural(/^(m|l)ouse$/i, '\1ice') + inflect.plural(/^(m|l)ice$/i, '\1ice') inflect.plural(/^(ox)$/i, '\1en') inflect.plural(/^(oxen)$/i, '\1') 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') @@ -35,13 +36,14 @@ module ActiveSupport inflect.singular(/(s)eries$/i, '\1eries') 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(/^(m|l)ice$/i, '\1ouse') + 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') @@ -56,6 +58,6 @@ module ActiveSupport inflect.irregular('cow', 'kine') inflect.irregular('zombie', 'zombies') - inflect.uncountable(%w(equipment information rice money species series fish sheep jeans)) + inflect.uncountable(%w(equipment information rice money species series fish sheep jeans police)) end end diff --git a/activesupport/lib/active_support/inflector/inflections.rb b/activesupport/lib/active_support/inflector/inflections.rb index 90bb62f57b..13b23d627a 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 @@ -26,6 +28,13 @@ module ActiveSupport @plurals, @singulars, @uncountables, @humans, @acronyms, @acronym_regex = [], [], [], [], {}, /(?=a)b/ end + # Private, for the test suite. + def initialize_dup(orig) + %w(plurals singulars uncountables humans acronyms acronym_regex).each do |scope| + instance_variable_set("@#{scope}", orig.send(scope).dup) + end + end + # Specifies a new acronym. An acronym must be specified as it will appear in a camelized string. An underscore # string that contains the acronym will retain the acronym when passed to `camelize`, `humanize`, or `titleize`. # A camelized string that contains the acronym will maintain the acronym when titleized or humanized, and will @@ -82,7 +91,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 +99,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 +143,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..4fcd32edf2 100644 --- a/activesupport/lib/active_support/inflector/methods.rb +++ b/activesupport/lib/active_support/inflector/methods.rb @@ -1,3 +1,5 @@ +# encoding: utf-8 + require 'active_support/inflector/inflections' module ActiveSupport @@ -42,10 +44,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 +68,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: @@ -75,7 +77,7 @@ module ActiveSupport # "SSLError".underscore.camelize # => "SslError" def underscore(camel_cased_word) word = camel_cased_word.to_s.dup - word.gsub!(/::/, '/') + word.gsub!('::', '/') word.gsub!(/(?:([A-Za-z\d])|^)(#{inflections.acronym_regex})(?=\b|[^a-z])/) { "#{$1}#{$1 && '_'}#{$2.downcase}" } word.gsub!(/([A-Z\d]+)([A-Z][a-z])/,'\1_\2') word.gsub!(/([a-z\d])([A-Z])/,'\1_\2') @@ -92,16 +94,19 @@ 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 # a nicer looking title. +titleize+ is meant for creating pretty output. It is not # used in the Rails internals. # - # +titleize+ is also aliased as as +titlecase+. + # +titleize+ is also aliased as +titlecase+. # # Examples: # "man from the boondocks".titleize # => "Man From The Boondocks" @@ -109,7 +114,7 @@ module ActiveSupport # "TheManWithoutAPast".titleize # => "The Man Without A Past" # "raiders_of_the_lost_ark".titleize # => "Raiders Of The Lost Ark" def titleize(word) - humanize(underscore(word)).gsub(/\b('?[a-z])/) { $1.capitalize } + humanize(underscore(word)).gsub(/\b(?<!['’`])[a-z]/) { $&.capitalize } end # Create the name of a table like Rails does for models to table names. This method @@ -141,9 +146,9 @@ module ActiveSupport # Replaces underscores with dashes in the string. # # Example: - # "puni_puni" # => "puni-puni" + # "puni_puni".dasherize # => "puni-puni" def dasherize(underscored_word) - underscored_word.gsub(/_/, '-') + underscored_word.tr('_', '-') end # Removes the module part from the expression in the string: @@ -207,11 +212,9 @@ module ActiveSupport names = camel_cased_word.split('::') names.shift if names.empty? || names.first.empty? - constant = Object - names.each do |name| - constant = constant.const_defined?(name, false) ? constant.const_get(name) : constant.const_missing(name) + names.inject(Object) do |constant, name| + constant.const_get(name, false) end - constant end # Tries to find a constant with the name specified in the argument string: @@ -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/decoding.rb b/activesupport/lib/active_support/json/decoding.rb index cbeb6c0a28..986a764479 100644 --- a/activesupport/lib/active_support/json/decoding.rb +++ b/activesupport/lib/active_support/json/decoding.rb @@ -9,7 +9,7 @@ module ActiveSupport module JSON class << self def decode(json, options ={}) - data = MultiJson.decode(json, options) + data = MultiJson.load(json, options) if ActiveSupport.parse_json_times convert_dates_from(data) else @@ -18,12 +18,12 @@ module ActiveSupport end def engine - MultiJson.engine + MultiJson.adapter end alias :backend :engine def engine=(name) - MultiJson.engine = name + MultiJson.use(name) end alias :backend= :engine= diff --git a/activesupport/lib/active_support/json/encoding.rb b/activesupport/lib/active_support/json/encoding.rb index 0e1bf4c40b..ef45a546b6 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 @@ -148,8 +147,8 @@ class Object end end -class Struct - def as_json(options = nil) #:nodoc: +class Struct #:nodoc: + def as_json(options = nil) Hash[members.zip(values)] end end @@ -183,6 +182,12 @@ class Numeric def encode_json(encoder) to_s end #:nodoc: end +class Float + # Encoding Infinity or NaN to JSON should return "null". The default returns + # "Infinity" or "NaN" what breaks parsing the JSON. E.g. JSON.parse('[NaN]'). + def as_json(options = nil) finite? ? self : NilClass::AS_JSON end #:nodoc: +end + class BigDecimal # A BigDecimal would be naturally represented as a JSON number. Most libraries, # however, parse non-integer JSON numbers directly as floats. Clients using @@ -192,7 +197,7 @@ class BigDecimal # That's why a JSON string is returned. The JSON literal is not numeric, but if # the other end knows by contract that the data is supposed to be a BigDecimal, # it still has the chance to post-process the string and get the real value. - def as_json(options = nil) to_s end #:nodoc: + def as_json(options = nil) finite? ? to_s : NilClass::AS_JSON end #:nodoc: end class Regexp @@ -239,8 +244,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/lazy_load_hooks.rb b/activesupport/lib/active_support/lazy_load_hooks.rb index 82507c1e03..c167efc1a7 100644 --- a/activesupport/lib/active_support/lazy_load_hooks.rb +++ b/activesupport/lib/active_support/lazy_load_hooks.rb @@ -1,32 +1,32 @@ -# lazy_load_hooks allows rails to lazily load a lot of components and thus making the app boot faster. Because of -# this feature now there is no need to require <tt>ActiveRecord::Base</tt> at boot time purely to apply configuration. Instead -# a hook is registered that applies configuration once <tt>ActiveRecord::Base</tt> is loaded. Here <tt>ActiveRecord::Base</tt> is used -# as example but this feature can be applied elsewhere too. -# -# Here is an example where +on_load+ method is called to register a hook. -# -# initializer "active_record.initialize_timezone" do -# ActiveSupport.on_load(:active_record) do -# self.time_zone_aware_attributes = true -# self.default_timezone = :utc -# end -# end -# -# When the entirety of +activerecord/lib/active_record/base.rb+ has been evaluated then +run_load_hooks+ is invoked. -# The very last line of +activerecord/lib/active_record/base.rb+ is: -# -# ActiveSupport.run_load_hooks(:active_record, ActiveRecord::Base) -# module ActiveSupport - @load_hooks = Hash.new {|h,k| h[k] = [] } - @loaded = {} + # lazy_load_hooks allows rails to lazily load a lot of components and thus making the app boot faster. Because of + # this feature now there is no need to require <tt>ActiveRecord::Base</tt> at boot time purely to apply configuration. Instead + # a hook is registered that applies configuration once <tt>ActiveRecord::Base</tt> is loaded. Here <tt>ActiveRecord::Base</tt> is used + # as example but this feature can be applied elsewhere too. + # + # Here is an example where +on_load+ method is called to register a hook. + # + # initializer "active_record.initialize_timezone" do + # ActiveSupport.on_load(:active_record) do + # self.time_zone_aware_attributes = true + # self.default_timezone = :utc + # end + # end + # + # When the entirety of +activerecord/lib/active_record/base.rb+ has been evaluated then +run_load_hooks+ is invoked. + # The very last line of +activerecord/lib/active_record/base.rb+ is: + # + # ActiveSupport.run_load_hooks(:active_record, ActiveRecord::Base) + # + @load_hooks = Hash.new { |h,k| h[k] = [] } + @loaded = Hash.new { |h,k| h[k] = [] } def self.on_load(name, options = {}, &block) - if base = @loaded[name] + @loaded[name].each do |base| execute_hook(base, options, block) - else - @load_hooks[name] << [block, options] end + + @load_hooks[name] << [block, options] end def self.execute_hook(base, options, block) @@ -38,7 +38,7 @@ module ActiveSupport end def self.run_load_hooks(name, base = Object) - @loaded[name] = base + @loaded[name] << base @load_hooks[name].each do |hook, options| execute_hook(base, options, hook) end diff --git a/activesupport/lib/active_support/log_subscriber.rb b/activesupport/lib/active_support/log_subscriber.rb index 6296c1d4b8..d2a6e1bd82 100644 --- a/activesupport/lib/active_support/log_subscriber.rb +++ b/activesupport/lib/active_support/log_subscriber.rb @@ -3,7 +3,7 @@ require 'active_support/core_ext/class/attribute' module ActiveSupport # ActiveSupport::LogSubscriber is an object set to consume ActiveSupport::Notifications - # with the sole purpose of logging them. The log subscriber dispatches notifications to + # with the sole purpose of logging them. The log subscriber dispatches notifications to # a registered object based on its given namespace. # # An example would be Active Record log subscriber responsible for logging queries: @@ -75,7 +75,8 @@ module ActiveSupport @@flushable_loggers ||= begin loggers = log_subscribers.map(&:logger) loggers.uniq! - loggers.select { |l| l.respond_to?(:flush) } + loggers.select! { |l| l.respond_to?(:flush) } + loggers end end @@ -92,7 +93,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 +101,8 @@ module ActiveSupport %w(info debug warn error fatal unknown).each do |level| class_eval <<-METHOD, __FILE__, __LINE__ + 1 - def #{level}(*args, &block) - return unless logger - logger.#{level}(*args, &block) + def #{level}(progname = nil, &block) + logger.#{level}(progname, &block) if logger 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 99b974e4a7..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' @@ -93,7 +94,7 @@ module ActiveSupport #:nodoc: chars(Unicode.unpack_graphemes(@wrapped_string).reverse.flatten.pack('U*')) end - # Limit the byte size of the string to a number of bytes without breaking characters. Usable + # Limits the byte size of the string to a number of bytes without breaking characters. Usable # when the storage for a string is limited for some reason. # # Example: @@ -102,7 +103,7 @@ module ActiveSupport #:nodoc: slice(0...translate_offset(limit)) end - # Convert characters in the string to uppercase. + # Converts characters in the string to uppercase. # # Example: # 'Laurent, où sont les tests ?'.mb_chars.upcase.to_s # => "LAURENT, OÙ SONT LES TESTS ?" @@ -110,7 +111,7 @@ module ActiveSupport #:nodoc: chars Unicode.upcase(@wrapped_string) end - # Convert characters in the string to lowercase. + # Converts characters in the string to lowercase. # # Example: # 'VĚDA A VÝZKUM'.mb_chars.downcase.to_s # => "věda a výzkum" @@ -118,6 +119,14 @@ module ActiveSupport #:nodoc: chars Unicode.downcase(@wrapped_string) end + # Converts characters in the string to the opposite case. + # + # Example: + # 'El Cañón".mb_chars.swapcase.to_s # => "eL cAÑÓN" + def swapcase + chars Unicode.swapcase(@wrapped_string) + end + # Converts the first character to uppercase and the remainder to lowercase. # # Example: @@ -180,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 94fb0a48aa..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. @@ -293,9 +293,17 @@ module ActiveSupport apply_mapping string, :uppercase_mapping end + def swapcase(string) + apply_mapping string, :swapcase_mapping + end + # Holds data about a codepoint in the Unicode database class Codepoint attr_accessor :code, :combining_class, :decomp_type, :decomp_mapping, :uppercase_mapping, :lowercase_mapping + + def swapcase_mapping + uppercase_mapping > 0 ? uppercase_mapping : lowercase_mapping + end end # Holds static data from the Unicode database diff --git a/activesupport/lib/active_support/notifications.rb b/activesupport/lib/active_support/notifications.rb index f549d2fff3..6735c561d3 100644 --- a/activesupport/lib/active_support/notifications.rb +++ b/activesupport/lib/active_support/notifications.rb @@ -1,7 +1,10 @@ +require 'active_support/notifications/instrumenter' +require 'active_support/notifications/fanout' + module ActiveSupport # = Notifications # - # +ActiveSupport::Notifications+ provides an instrumentation API for Ruby. + # <tt>ActiveSupport::Notifications</tt> provides an instrumentation API for Ruby. # # == Instrumenters # @@ -41,26 +44,53 @@ module ActiveSupport # event.duration # => 10 (in milliseconds) # event.payload # => { :extra => :information } # - # The block in the +subscribe+ call gets the name of the event, start + # The block in the <tt>subscribe</tt> call gets the name of the event, start # timestamp, end timestamp, a string with a unique identifier for that event # (something like "535801666f04d0298cd6"), and a hash with the payload, in # that order. # # If an exception happens during that particular instrumentation the payload will - # have a key +:exception+ with an array of two elements as value: a string with + # have a key <tt>:exception</tt> with an array of two elements as value: a string with # the name of the exception class, and the exception message. # - # As the previous example depicts, the class +ActiveSupport::Notifications::Event+ + # As the previous example depicts, the class <tt>ActiveSupport::Notifications::Event</tt> # is able to take the arguments as they come and provide an object-oriented # interface to that data. # + # It is also possible to pass an object as the second parameter passed to the + # <tt>subscribe</tt> method instead of a block: + # + # module ActionController + # class PageRequest + # def call(name, started, finished, unique_id, payload) + # Rails.logger.debug ["notification:", name, started, finished, unique_id, payload].join(" ") + # end + # end + # end + # + # ActiveSupport::Notifications.subscribe('process_action.action_controller', ActionController::PageRequest.new) + # + # resulting in the following output within the logs including a hash with the payload: + # + # notification: process_action.action_controller 2012-04-13 01:08:35 +0300 2012-04-13 01:08:35 +0300 af358ed7fab884532ec7 { + # :controller=>"Devise::SessionsController", + # :action=>"new", + # :params=>{"action"=>"new", "controller"=>"devise/sessions"}, + # :format=>:html, + # :method=>"GET", + # :path=>"/login/sign_in", + # :status=>200, + # :view_runtime=>279.3080806732178, + # :db_runtime=>40.053 + # } + # # You can also subscribe to all events whose name matches a certain regexp: # # ActiveSupport::Notifications.subscribe(/render/) do |*args| # ... # end # - # and even pass no argument to +subscribe+, in which case you are subscribing + # and even pass no argument to <tt>subscribe</tt>, in which case you are subscribing # to all events. # # == Temporary Subscriptions @@ -68,6 +98,10 @@ module ActiveSupport # Sometimes you do not want to subscribe to an event for the entire life of # the application. There are two ways to unsubscribe. # + # WARNING: The instrumentation framework is designed for long-running subscribers, + # use this feature sparingly because it wipes some internal caches and that has + # a negative impact on performance. + # # === Subscribe While a Block Runs # # You can subscribe to some event temporarily while some block runs. For @@ -101,10 +135,6 @@ module ActiveSupport # to log subscribers in a thread. You can use any queue implementation you want. # module Notifications - autoload :Instrumenter, 'active_support/notifications/instrumenter' - autoload :Event, 'active_support/notifications/instrumenter' - autoload :Fanout, 'active_support/notifications/fanout' - @instrumenters = Hash.new { |h,k| h[k] = notifier.listening?(k) } class << self diff --git a/activesupport/lib/active_support/notifications/fanout.rb b/activesupport/lib/active_support/notifications/fanout.rb index a9aa5464e9..17c99089c1 100644 --- a/activesupport/lib/active_support/notifications/fanout.rb +++ b/activesupport/lib/active_support/notifications/fanout.rb @@ -9,18 +9,25 @@ module ActiveSupport end def subscribe(pattern = nil, block = Proc.new) - subscriber = Subscriber.new(pattern, block).tap do |s| - @subscribers << s - end + subscriber = Subscribers.new pattern, block + @subscribers << subscriber @listeners_for.clear subscriber end def unsubscribe(subscriber) - @subscribers.reject! {|s| s.matches?(subscriber)} + @subscribers.reject! { |s| s.matches?(subscriber) } @listeners_for.clear end + def start(name, id, payload) + listeners_for(name).each { |s| s.start(name, id, payload) } + end + + def finish(name, id, payload) + listeners_for(name).each { |s| s.finish(name, id, payload) } + end + def publish(name, *args) listeners_for(name).each { |s| s.publish(name, *args) } end @@ -37,23 +44,89 @@ module ActiveSupport def wait end - class Subscriber #:nodoc: - def initialize(pattern, delegate) - @pattern = pattern - @delegate = delegate + module Subscribers # :nodoc: + def self.new(pattern, listener) + if listener.respond_to?(:call) + subscriber = Timed.new pattern, listener + else + subscriber = Evented.new pattern, listener + end + + unless pattern + AllMessages.new(subscriber) + else + subscriber + end end - def publish(message, *args) - @delegate.call(message, *args) + class Evented #:nodoc: + def initialize(pattern, delegate) + @pattern = pattern + @delegate = delegate + end + + def start(name, id, payload) + @delegate.start name, id, payload + end + + def finish(name, id, payload) + @delegate.finish name, id, payload + end + + def subscribed_to?(name) + @pattern === name.to_s + end + + def matches?(subscriber_or_name) + self === subscriber_or_name || + @pattern && @pattern === subscriber_or_name + end end - def subscribed_to?(name) - !@pattern || @pattern === name.to_s + class Timed < Evented + def initialize(pattern, delegate) + @timestack = Hash.new { |h,id| + h[id] = Hash.new { |ids,name| ids[name] = [] } + } + super + end + + def publish(name, *args) + @delegate.call name, *args + end + + def start(name, id, payload) + @timestack[id][name].push Time.now + end + + def finish(name, id, payload) + started = @timestack[id][name].pop + @delegate.call(name, started, Time.now, id, payload) + end end - def matches?(subscriber_or_name) - self === subscriber_or_name || - @pattern && @pattern === subscriber_or_name + class AllMessages # :nodoc: + def initialize(delegate) + @delegate = delegate + end + + def start(name, id, payload) + @delegate.start name, id, payload + end + + def finish(name, id, payload) + @delegate.finish name, id, payload + end + + def publish(name, *args) + @delegate.publish name, *args + end + + def subscribed_to?(name) + true + end + + alias :matches? :=== end end end diff --git a/activesupport/lib/active_support/notifications/instrumenter.rb b/activesupport/lib/active_support/notifications/instrumenter.rb index 3941c285a2..58e292c658 100644 --- a/activesupport/lib/active_support/notifications/instrumenter.rb +++ b/activesupport/lib/active_support/notifications/instrumenter.rb @@ -1,7 +1,6 @@ -require 'active_support/core_ext/module/delegation' - module ActiveSupport module Notifications + # Instrumentors are stored in a thread local. class Instrumenter attr_reader :id @@ -14,15 +13,14 @@ module ActiveSupport # and publish it. Notice that events get sent even if an error occurs # in the passed-in block def instrument(name, payload={}) - started = Time.now - + @notifier.start(name, @id, payload) begin yield rescue Exception => e payload[:exception] = [e.class.name, e.message] raise e ensure - @notifier.publish(name, started, Time.now, @id, payload) + @notifier.finish(name, @id, payload) end end diff --git a/activesupport/lib/active_support/ordered_options.rb b/activesupport/lib/active_support/ordered_options.rb index bf81567d22..538e41e0eb 100644 --- a/activesupport/lib/active_support/ordered_options.rb +++ b/activesupport/lib/active_support/ordered_options.rb @@ -1,5 +1,3 @@ -require 'active_support/ordered_hash' - # Usually key value pairs are handled something like this: # # h = {} @@ -17,7 +15,7 @@ require 'active_support/ordered_hash' # h.girl # => 'Mary' # module ActiveSupport #:nodoc: - class OrderedOptions < OrderedHash + class OrderedOptions < Hash alias_method :_get, :[] # preserve the original #[] method protected :_get # make it protected @@ -30,8 +28,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/railtie.rb b/activesupport/lib/active_support/railtie.rb index f696716cc8..d1c62c5087 100644 --- a/activesupport/lib/active_support/railtie.rb +++ b/activesupport/lib/active_support/railtie.rb @@ -8,30 +8,6 @@ module ActiveSupport initializer "active_support.deprecation_behavior" do |app| if deprecation = app.config.active_support.deprecation ActiveSupport::Deprecation.behavior = deprecation - else - defaults = {"development" => :log, - "production" => :notify, - "test" => :stderr} - - env = Rails.env - - if defaults.key?(env) - msg = "You did not specify how you would like Rails to report " \ - "deprecation notices for your #{env} environment, please " \ - "set config.active_support.deprecation to :#{defaults[env]} " \ - "at config/environments/#{env}.rb" - - warn msg - ActiveSupport::Deprecation.behavior = defaults[env] - else - msg = "You did not specify how you would like Rails to report " \ - "deprecation notices for your #{env} environment, please " \ - "set config.active_support.deprecation to :log, :notify or " \ - ":stderr at config/environments/#{env}.rb" - - warn msg - ActiveSupport::Deprecation.behavior = :stderr - end end end @@ -42,8 +18,7 @@ module ActiveSupport zone_default = Time.find_zone!(app.config.time_zone) unless zone_default - raise \ - 'Value assigned to config.time_zone not recognized.' + + raise 'Value assigned to config.time_zone not recognized. ' \ 'Run "rake -D time" for a list of tasks for finding appropriate time zone names.' end diff --git a/activesupport/lib/active_support/rescuable.rb b/activesupport/lib/active_support/rescuable.rb index 0f4a06468a..7aecdd11d3 100644 --- a/activesupport/lib/active_support/rescuable.rb +++ b/activesupport/lib/active_support/rescuable.rb @@ -48,6 +48,7 @@ module ActiveSupport # end # end # + # Exceptions raised inside exception handlers are not propagated up. def rescue_from(*klasses, &block) options = klasses.extract_options! @@ -108,7 +109,11 @@ module ActiveSupport when Symbol method(rescuer) when Proc - rescuer.bind(self) + if rescuer.arity == 0 + Proc.new { instance_exec(&rescuer) } + else + Proc.new { |_exception| instance_exec(_exception, &rescuer) } + end end end end diff --git a/activesupport/lib/active_support/ruby/shim.rb b/activesupport/lib/active_support/ruby/shim.rb index 41fd866481..13e96b3596 100644 --- a/activesupport/lib/active_support/ruby/shim.rb +++ b/activesupport/lib/active_support/ruby/shim.rb @@ -12,5 +12,4 @@ require 'active_support/core_ext/date_time/conversions' require 'active_support/core_ext/enumerable' require 'active_support/core_ext/string/conversions' require 'active_support/core_ext/string/interpolation' -require 'active_support/core_ext/string/encoding' require 'active_support/core_ext/time/conversions' diff --git a/activesupport/lib/active_support/string_inquirer.rb b/activesupport/lib/active_support/string_inquirer.rb index e6b1f39225..f3f3909a90 100644 --- a/activesupport/lib/active_support/string_inquirer.rb +++ b/activesupport/lib/active_support/string_inquirer.rb @@ -11,8 +11,8 @@ module ActiveSupport # class StringInquirer < String def method_missing(method_name, *arguments) - if method_name.to_s[-1,1] == "?" - self == method_name.to_s[0..-2] + if method_name[-1, 1] == "?" + self == method_name[0..-2] else super end diff --git a/activesupport/lib/active_support/tagged_logging.rb b/activesupport/lib/active_support/tagged_logging.rb index dc3ca25938..538a36f6d9 100644 --- a/activesupport/lib/active_support/tagged_logging.rb +++ b/activesupport/lib/active_support/tagged_logging.rb @@ -1,63 +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 - @tags = Hash.new { |h,k| h[k] = [] } - end - - def tagged(*new_tags) - tags = current_tags - new_tags = Array(new_tags).flatten.reject(&:blank?) - tags.concat new_tags - yield - ensure - new_tags.size.times { tags.pop } - end + module TaggedLogging + module Formatter # :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 add(severity, message = nil, progname = nil, &block) - @logger.add(severity, "#{tags_text}#{message}", progname, &block) - end + def clear! + current_tags.clear + 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 current_tags + Thread.current[:activesupport_tagged_logging_tags] ||= [] + end - def flush - @tags.delete(Thread.current) - @logger.flush if @logger.respond_to?(:flush) + private + def tags_text + tags = current_tags + if tags.any? + tags.collect { |tag| "[#{tag}] " }.join + end + end end - def method_missing(method, *args) - @logger.send(method, *args) + def self.new(logger) + logger.formatter.extend Formatter + logger.extend(self) end - protected - - def tags_text - tags = current_tags - if tags.any? - tags.collect { |tag| "[#{tag}]" }.join(" ") + " " - end + def tagged(*new_tags) + tags = formatter.current_tags + new_tags = new_tags.flatten.reject(&:blank?) + tags.concat new_tags + yield self + ensure + tags.pop(new_tags.size) end - def current_tags - @tags[Thread.current] + def flush + 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 707544e594..9a52c916ec 100644 --- a/activesupport/lib/active_support/test_case.rb +++ b/activesupport/lib/active_support/test_case.rb @@ -1,15 +1,26 @@ -require 'test/unit/testcase' +require 'minitest/spec' 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' module ActiveSupport - class TestCase < ::Test::Unit::TestCase + class TestCase < ::MiniTest::Spec + + if MiniTest::Unit::VERSION < '2.6.1' + class << self + alias :name :to_s + end + end + + # Use AS::TestCase for the base class when describing a model + register_spec_type(self) do |desc| + desc < ActiveRecord::Model + end + Assertion = MiniTest::Assertion alias_method :method_name, :name if method_defined? :name alias_method :method_name, :__name__ if method_defined? :__name__ @@ -19,10 +30,26 @@ module ActiveSupport yield if $tags[tag] end + # FIXME: we have tests that depend on run order, we should fix that and + # remove this method. + def self.test_order # :nodoc: + :sorted + end + 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 + alias :assert_raise :assert_raises + alias :assert_not_nil :refute_nil + alias :assert_not_equal :refute_equal + alias :assert_no_match :refute_match + alias :assert_not_same :refute_same + + def assert_nothing_raised(*args) + yield + end end end diff --git a/activesupport/lib/active_support/testing/deprecation.rb b/activesupport/lib/active_support/testing/deprecation.rb index 0135185a47..a8342904dc 100644 --- a/activesupport/lib/active_support/testing/deprecation.rb +++ b/activesupport/lib/active_support/testing/deprecation.rb @@ -34,22 +34,3 @@ module ActiveSupport end end end - -begin - require 'test/unit/error' -rescue LoadError - # Using miniunit, ignore. -else - module Test - module Unit - class Error #:nodoc: - # Silence warnings when reporting test errors. - def message_with_silenced_deprecation - ActiveSupport::Deprecation.silence { message_without_silenced_deprecation } - end - alias_method :message_without_silenced_deprecation, :message - alias_method :message, :message_with_silenced_deprecation - end - end - end -end 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/jruby.rb b/activesupport/lib/active_support/testing/performance/jruby.rb index b347539f13..34e3f9f45f 100644 --- a/activesupport/lib/active_support/testing/performance/jruby.rb +++ b/activesupport/lib/active_support/testing/performance/jruby.rb @@ -42,7 +42,7 @@ module ActiveSupport klasses.each do |klass| fname = output_filename(klass) FileUtils.mkdir_p(File.dirname(fname)) - file = File.open(fname, 'wb') do |file| + File.open(fname, 'wb') do |file| klass.new(@data).printProfile(file) end end 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/testing/setup_and_teardown.rb b/activesupport/lib/active_support/testing/setup_and_teardown.rb index 40b781485e..772c7b4209 100644 --- a/activesupport/lib/active_support/testing/setup_and_teardown.rb +++ b/activesupport/lib/active_support/testing/setup_and_teardown.rb @@ -10,7 +10,6 @@ module ActiveSupport include ActiveSupport::Callbacks define_callbacks :setup, :teardown - include ForMiniTest end module ClassMethods @@ -23,24 +22,22 @@ module ActiveSupport end end - module ForMiniTest - def run(runner) - result = '.' + def run(runner) + result = '.' + begin + run_callbacks :setup do + result = super + end + rescue Exception => e + result = runner.puke(self.class, method_name, e) + ensure begin - run_callbacks :setup do - result = super - end + run_callbacks :teardown rescue Exception => e result = runner.puke(self.class, method_name, e) - ensure - begin - run_callbacks :teardown - rescue Exception => e - result = runner.puke(self.class, method_name, e) - end end - result end + result end end diff --git a/activesupport/lib/active_support/time/autoload.rb b/activesupport/lib/active_support/time/autoload.rb deleted file mode 100644 index c9a7731b39..0000000000 --- a/activesupport/lib/active_support/time/autoload.rb +++ /dev/null @@ -1,5 +0,0 @@ -module ActiveSupport - autoload :Duration, 'active_support/duration' - autoload :TimeWithZone, 'active_support/time_with_zone' - autoload :TimeZone, 'active_support/values/time_zone' -end diff --git a/activesupport/lib/active_support/values/time_zone.rb b/activesupport/lib/active_support/values/time_zone.rb index 35f400f9df..9543e50395 100644 --- a/activesupport/lib/active_support/values/time_zone.rb +++ b/activesupport/lib/active_support/values/time_zone.rb @@ -1,34 +1,34 @@ require 'active_support/core_ext/object/blank' require 'active_support/core_ext/object/try' -# The TimeZone class serves as a wrapper around TZInfo::Timezone instances. It allows us to do the following: -# -# * Limit the set of zones provided by TZInfo to a meaningful subset of 142 zones. -# * Retrieve and display zones with a friendlier name (e.g., "Eastern Time (US & Canada)" instead of "America/New_York"). -# * Lazily load TZInfo::Timezone instances only when they're needed. -# * Create ActiveSupport::TimeWithZone instances via TimeZone's +local+, +parse+, +at+ and +now+ methods. -# -# If you set <tt>config.time_zone</tt> in the Rails Application, you can access this TimeZone object via <tt>Time.zone</tt>: -# -# # application.rb: -# class Application < Rails::Application -# config.time_zone = "Eastern Time (US & Canada)" -# end -# -# Time.zone # => #<TimeZone:0x514834...> -# Time.zone.name # => "Eastern Time (US & Canada)" -# Time.zone.now # => Sun, 18 May 2008 14:30:44 EDT -04:00 -# -# The version of TZInfo bundled with Active Support only includes the definitions necessary to support the zones -# defined by the TimeZone class. If you need to use zones that aren't defined by TimeZone, you'll need to install the TZInfo gem -# (if a recent version of the gem is installed locally, this will be used instead of the bundled version.) module ActiveSupport + # The TimeZone class serves as a wrapper around TZInfo::Timezone instances. It allows us to do the following: + # + # * Limit the set of zones provided by TZInfo to a meaningful subset of 142 zones. + # * Retrieve and display zones with a friendlier name (e.g., "Eastern Time (US & Canada)" instead of "America/New_York"). + # * Lazily load TZInfo::Timezone instances only when they're needed. + # * Create ActiveSupport::TimeWithZone instances via TimeZone's +local+, +parse+, +at+ and +now+ methods. + # + # If you set <tt>config.time_zone</tt> in the Rails Application, you can access this TimeZone object via <tt>Time.zone</tt>: + # + # # application.rb: + # class Application < Rails::Application + # config.time_zone = "Eastern Time (US & Canada)" + # end + # + # Time.zone # => #<TimeZone:0x514834...> + # Time.zone.name # => "Eastern Time (US & Canada)" + # Time.zone.now # => Sun, 18 May 2008 14:30:44 EDT -04:00 + # + # The version of TZInfo bundled with Active Support only includes the definitions necessary to support the zones + # defined by the TimeZone class. If you need to use zones that aren't defined by TimeZone, you'll need to install the TZInfo gem + # (if a recent version of the gem is installed locally, this will be used instead of the bundled version.) class TimeZone # Keys are Rails TimeZone names, values are TZInfo identifiers MAPPING = { "International Date Line West" => "Pacific/Midway", "Midway Island" => "Pacific/Midway", - "Samoa" => "Pacific/Pago_Pago", + "American Samoa" => "Pacific/Pago_Pago", "Hawaii" => "Pacific/Honolulu", "Alaska" => "America/Juneau", "Pacific Time (US & Canada)" => "America/Los_Angeles", @@ -167,9 +167,10 @@ module ActiveSupport "Marshall Is." => "Pacific/Majuro", "Auckland" => "Pacific/Auckland", "Wellington" => "Pacific/Auckland", - "Nuku'alofa" => "Pacific/Tongatapu" - }.each { |name, zone| name.freeze; zone.freeze } - MAPPING.freeze + "Nuku'alofa" => "Pacific/Tongatapu", + "Tokelau Is." => "Pacific/Fakaofo", + "Samoa" => "Pacific/Apia" + } UTC_OFFSET_WITH_COLON = '%s%02d:%02d' UTC_OFFSET_WITHOUT_COLON = UTC_OFFSET_WITH_COLON.sub(':', '') @@ -203,6 +204,7 @@ module ActiveSupport @current_period = nil end + # Returns the offset of this time zone from UTC in seconds. def utc_offset if @utc_offset @utc_offset @@ -267,7 +269,7 @@ module ActiveSupport # Time.zone.parse('22:30:00') # => Fri, 31 Dec 1999 22:30:00 HST -10:00 def parse(str, now=now) date_parts = Date._parse(str) - return if date_parts.blank? + return if date_parts.empty? time = Time.parse(str, now) rescue DateTime.parse(str) if date_parts[:offset].nil? ActiveSupport::TimeWithZone.new(nil, self, time) @@ -282,7 +284,7 @@ module ActiveSupport # Time.zone = 'Hawaii' # => "Hawaii" # Time.zone.now # => Wed, 23 Jan 2008 20:24:27 HST -10:00 def now - Time.now.utc.in_time_zone(self) + time_now.utc.in_time_zone(self) end # Return the current date in this time zone. @@ -391,5 +393,11 @@ module ActiveSupport end end end + + private + + def time_now + Time.now + 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..4551dd2f2d 100644 --- a/activesupport/lib/active_support/xml_mini/jdom.rb +++ b/activesupport/lib/active_support/xml_mini/jdom.rb @@ -12,7 +12,6 @@ java_import org.xml.sax.InputSource unless defined? InputSource java_import org.xml.sax.Attributes unless defined? Attributes java_import org.w3c.dom.Node unless defined? Node -# = XmlMini JRuby JDOM implementation module ActiveSupport module XmlMini_JDOM #:nodoc: extend self @@ -71,7 +70,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 +132,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 +146,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 +162,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 diff --git a/activesupport/lib/active_support/xml_mini/libxml.rb b/activesupport/lib/active_support/xml_mini/libxml.rb index 16570c6aea..26556598fd 100644 --- a/activesupport/lib/active_support/xml_mini/libxml.rb +++ b/activesupport/lib/active_support/xml_mini/libxml.rb @@ -2,7 +2,6 @@ require 'libxml' require 'active_support/core_ext/object/blank' require 'stringio' -# = XmlMini LibXML implementation module ActiveSupport module XmlMini_LibXML #:nodoc: extend self diff --git a/activesupport/lib/active_support/xml_mini/libxmlsax.rb b/activesupport/lib/active_support/xml_mini/libxmlsax.rb index 2536b1f33e..acc018fd2d 100644 --- a/activesupport/lib/active_support/xml_mini/libxmlsax.rb +++ b/activesupport/lib/active_support/xml_mini/libxmlsax.rb @@ -2,9 +2,8 @@ require 'libxml' require 'active_support/core_ext/object/blank' require 'stringio' -# = XmlMini LibXML implementation using a SAX-based parser module ActiveSupport - module XmlMini_LibXMLSAX + module XmlMini_LibXMLSAX #:nodoc: extend self # Class that will build the hash while the XML document diff --git a/activesupport/lib/active_support/xml_mini/nokogiri.rb b/activesupport/lib/active_support/xml_mini/nokogiri.rb index 04ec9e8ab8..bb0a52bdcf 100644 --- a/activesupport/lib/active_support/xml_mini/nokogiri.rb +++ b/activesupport/lib/active_support/xml_mini/nokogiri.rb @@ -7,7 +7,6 @@ end require 'active_support/core_ext/object/blank' require 'stringio' -# = XmlMini Nokogiri implementation module ActiveSupport module XmlMini_Nokogiri #:nodoc: extend self diff --git a/activesupport/lib/active_support/xml_mini/nokogirisax.rb b/activesupport/lib/active_support/xml_mini/nokogirisax.rb index 93fd3dfe57..30b94aac47 100644 --- a/activesupport/lib/active_support/xml_mini/nokogirisax.rb +++ b/activesupport/lib/active_support/xml_mini/nokogirisax.rb @@ -7,9 +7,8 @@ end require 'active_support/core_ext/object/blank' require 'stringio' -# = XmlMini Nokogiri implementation using a SAX-based parser module ActiveSupport - module XmlMini_NokogiriSAX + module XmlMini_NokogiriSAX #:nodoc: extend self # Class that will build the hash while the XML document diff --git a/activesupport/lib/active_support/xml_mini/rexml.rb b/activesupport/lib/active_support/xml_mini/rexml.rb index a13ad10118..a2a87337a6 100644 --- a/activesupport/lib/active_support/xml_mini/rexml.rb +++ b/activesupport/lib/active_support/xml_mini/rexml.rb @@ -2,7 +2,6 @@ require 'active_support/core_ext/kernel/reporting' require 'active_support/core_ext/object/blank' require 'stringio' -# = XmlMini ReXML implementation module ActiveSupport module XmlMini_REXML #:nodoc: extend self |