diff options
Diffstat (limited to 'activesupport/lib/active_support')
16 files changed, 193 insertions, 796 deletions
diff --git a/activesupport/lib/active_support/cache.rb b/activesupport/lib/active_support/cache.rb index 5a5548d567..edbe697962 100644 --- a/activesupport/lib/active_support/cache.rb +++ b/activesupport/lib/active_support/cache.rb @@ -275,34 +275,14 @@ module ActiveSupport if block_given? options = merged_options(options) key = namespaced_key(name, options) - unless options[:force] - entry = instrument(:read, name, options) do |payload| - payload[:super_operation] = :fetch if payload - read_entry(key, options) - end - end - if entry && entry.expired? - race_ttl = options[:race_condition_ttl].to_i - if race_ttl && (Time.now - entry.expires_at <= race_ttl) - # When an entry has :race_condition_ttl defined, put the stale entry back into the cache - # for a brief period while the entry is begin recalculated. - entry.expires_at = Time.now + race_ttl - write_entry(key, entry, :expires_in => race_ttl * 2) - else - delete_entry(key, options) - end - entry = nil - end + + cached_entry = find_cached_entry(key, name, options) unless options[:force] + entry = handle_expired_entry(cached_entry, key, options) if entry - instrument(:fetch_hit, name, options) { |payload| } - entry.value + get_entry_value(entry, name, options) else - result = instrument(:generate, name, options) do |payload| - yield(name) - end - write(name, result, options) - result + save_block_result_to_cache(name, options) { |_name| yield _name } end else read(name, options) @@ -531,6 +511,42 @@ module ActiveSupport return unless logger && logger.debug? && !silence? logger.debug("Cache #{operation}: #{key}#{options.blank? ? "" : " (#{options.inspect})"}") end + + def find_cached_entry(key, name, options) + instrument(:read, name, options) do |payload| + payload[:super_operation] = :fetch if payload + read_entry(key, options) + end + end + + def handle_expired_entry(entry, key, options) + if entry && entry.expired? + race_ttl = options[:race_condition_ttl].to_i + if race_ttl && (Time.now - entry.expires_at <= race_ttl) + # When an entry has :race_condition_ttl defined, put the stale entry back into the cache + # for a brief period while the entry is begin recalculated. + entry.expires_at = Time.now + race_ttl + write_entry(key, entry, :expires_in => race_ttl * 2) + else + delete_entry(key, options) + end + entry = nil + end + entry + end + + def get_entry_value(entry, name, options) + instrument(:fetch_hit, name, options) { |payload| } + entry.value + end + + def save_block_result_to_cache(name, options) + result = instrument(:generate, name, options) do |payload| + yield(name) + end + write(name, result, options) + result + end end # This class is used to represent cache entries. Cache entries have a value and an optional diff --git a/activesupport/lib/active_support/core_ext/array/access.rb b/activesupport/lib/active_support/core_ext/array/access.rb index a8f9dddae5..4f1e432b61 100644 --- a/activesupport/lib/active_support/core_ext/array/access.rb +++ b/activesupport/lib/active_support/core_ext/array/access.rb @@ -21,28 +21,28 @@ class Array # Equal to <tt>self[1]</tt>. # - # %w( a b c d e).second # => "b" + # %w( a b c d e ).second # => "b" def second self[1] end # Equal to <tt>self[2]</tt>. # - # %w( a b c d e).third # => "c" + # %w( a b c d e ).third # => "c" def third self[2] end # Equal to <tt>self[3]</tt>. # - # %w( a b c d e).fourth # => "d" + # %w( a b c d e ).fourth # => "d" def fourth self[3] end # Equal to <tt>self[4]</tt>. # - # %w( a b c d e).fifth # => "e" + # %w( a b c d e ).fifth # => "e" def fifth self[4] end diff --git a/activesupport/lib/active_support/core_ext/date/conversions.rb b/activesupport/lib/active_support/core_ext/date/conversions.rb index fe08ade7e0..cdf606f28c 100644 --- a/activesupport/lib/active_support/core_ext/date/conversions.rb +++ b/activesupport/lib/active_support/core_ext/date/conversions.rb @@ -17,10 +17,10 @@ class Date } # Ruby 1.9 has Date#to_time which converts to localtime only. - remove_possible_method :to_time + remove_method :to_time # Ruby 1.9 has Date#xmlschema which converts to a string without the time component. - remove_possible_method :xmlschema + remove_method :xmlschema # Convert to a formatted string. See DATE_FORMATS for predefined formats. # diff --git a/activesupport/lib/active_support/core_ext/hash/conversions.rb b/activesupport/lib/active_support/core_ext/hash/conversions.rb index 6cb7434e5f..8930376ac8 100644 --- a/activesupport/lib/active_support/core_ext/hash/conversions.rb +++ b/activesupport/lib/active_support/core_ext/hash/conversions.rb @@ -101,17 +101,33 @@ class Hash # # hash = Hash.from_xml(xml) # # => {"hash"=>{"foo"=>1, "bar"=>2}} - def from_xml(xml) - ActiveSupport::XMLConverter.new(xml).to_h + # + # DisallowedType is raise if the XML contains attributes with <tt>type="yaml"</tt> or + # <tt>type="symbol"</tt>. Use <tt>Hash.from_trusted_xml</tt> to parse this XML. + def from_xml(xml, disallowed_types = nil) + ActiveSupport::XMLConverter.new(xml, disallowed_types).to_h end + # Builds a Hash from XML just like <tt>Hash.from_xml</tt>, but also allows Symbol and YAML. + def from_trusted_xml(xml) + from_xml xml, [] + end end end module ActiveSupport class XMLConverter # :nodoc: - def initialize(xml) + class DisallowedType < StandardError + def initialize(type) + super "Disallowed type attribute: #{type.inspect}" + end + end + + DISALLOWED_TYPES = %w(symbol yaml) + + def initialize(xml, disallowed_types = nil) @xml = normalize_keys(XmlMini.parse(xml)) + @disallowed_types = disallowed_types || DISALLOWED_TYPES end def to_h @@ -119,7 +135,6 @@ module ActiveSupport end private - def normalize_keys(params) case params when Hash @@ -145,6 +160,10 @@ module ActiveSupport end def process_hash(value) + if value.include?('type') && !value['type'].is_a?(Hash) && @disallowed_types.include?(value['type']) + raise DisallowedType, value['type'] + end + if become_array?(value) _, entries = Array.wrap(value.detect { |k,v| not v.is_a?(String) }) if entries.nil? || value['__content__'].try(:empty?) diff --git a/activesupport/lib/active_support/core_ext/string/conversions.rb b/activesupport/lib/active_support/core_ext/string/conversions.rb index c795df124b..428fa1f826 100644 --- a/activesupport/lib/active_support/core_ext/string/conversions.rb +++ b/activesupport/lib/active_support/core_ext/string/conversions.rb @@ -3,26 +3,36 @@ require 'active_support/core_ext/time/calculations' class String # Converts a string to a Time value. - # The +form+ can be either :utc or :local (default :utc). + # The +form+ can be either :utc or :local (default :local). # - # The time is parsed using Date._parse method. - # If +form+ is :local, then time is formatted using Time.zone + # The time is parsed using Time.parse method. + # If +form+ is :local, then the time is in the system timezone. + # If the date part is missing then the current date is used and if + # the time part is missing then it is assumed to be 00:00:00. # - # "3-2-2012".to_time # => 2012-02-03 00:00:00 UTC - # "12:20".to_time # => ArgumentError: invalid date - # "2012-12-13 06:12".to_time # => 2012-12-13 06:12:00 UTC - # "2012-12-13T06:12".to_time # => 2012-12-13 06:12:00 UTC - # "2012-12-13T06:12".to_time(:local) # => 2012-12-13 06:12:00 +0100 - def to_time(form = :utc) - 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 + # "13-12-2012".to_time # => 2012-12-13 00:00:00 +0100 + # "06:12".to_time # => 2012-12-13 06:12:00 +0100 + # "2012-12-13 06:12".to_time # => 2012-12-13 06:12:00 +0100 + # "2012-12-13T06:12".to_time # => 2012-12-13 06:12:00 +0100 + # "2012-12-13T06:12".to_time(:utc) # => 2012-12-13 05:12:00 UTC + def to_time(form = :local) + parts = Date._parse(self, false) + return if parts.empty? - ::Time.send(form, *date_values) - offset - end + now = Time.now + offset = parts[:offset] + utc_offset = form == :utc ? 0 : now.utc_offset + adjustment = offset ? offset - utc_offset : 0 + + Time.send( + form, + parts.fetch(:year, now.year), + parts.fetch(:mon, now.month), + parts.fetch(:mday, now.day), + parts.fetch(:hour, 0), + parts.fetch(:min, 0), + parts.fetch(:sec, 0) + parts.fetch(:sec_fraction, 0) + ) - adjustment end # Converts a string to a Date value. @@ -42,13 +52,6 @@ class String # "2012-12-13 12:50".to_datetime #=> Thu, 13 Dec 2012 12:50:00 +0000 # "12/13/2012".to_datetime #=> ArgumentError: invalid date def to_datetime - 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 + ::DateTime.parse(self, false) unless blank? end end diff --git a/activesupport/lib/active_support/inflector/inflections.rb b/activesupport/lib/active_support/inflector/inflections.rb index 9cf4b2b2ba..c96debb93f 100644 --- a/activesupport/lib/active_support/inflector/inflections.rb +++ b/activesupport/lib/active_support/inflector/inflections.rb @@ -128,17 +128,29 @@ module ActiveSupport def irregular(singular, plural) @uncountables.delete(singular) @uncountables.delete(plural) - if singular[0,1].upcase == plural[0,1].upcase - plural(Regexp.new("(#{singular[0,1]})#{singular[1..-1]}$", "i"), '\1' + plural[1..-1]) - plural(Regexp.new("(#{plural[0,1]})#{plural[1..-1]}$", "i"), '\1' + plural[1..-1]) - singular(Regexp.new("(#{plural[0,1]})#{plural[1..-1]}$", "i"), '\1' + singular[1..-1]) + + s0 = singular[0] + srest = singular[1..-1] + + p0 = plural[0] + prest = plural[1..-1] + + if s0.upcase == p0.upcase + plural(/(#{s0})#{srest}$/i, '\1' + prest) + plural(/(#{p0})#{prest}$/i, '\1' + prest) + + singular(/(#{s0})#{srest}$/i, '\1' + srest) + singular(/(#{p0})#{prest}$/i, '\1' + srest) else - plural(Regexp.new("#{singular[0,1].upcase}(?i)#{singular[1..-1]}$"), plural[0,1].upcase + plural[1..-1]) - plural(Regexp.new("#{singular[0,1].downcase}(?i)#{singular[1..-1]}$"), plural[0,1].downcase + plural[1..-1]) - plural(Regexp.new("#{plural[0,1].upcase}(?i)#{plural[1..-1]}$"), plural[0,1].upcase + plural[1..-1]) - plural(Regexp.new("#{plural[0,1].downcase}(?i)#{plural[1..-1]}$"), plural[0,1].downcase + plural[1..-1]) - singular(Regexp.new("#{plural[0,1].upcase}(?i)#{plural[1..-1]}$"), singular[0,1].upcase + singular[1..-1]) - singular(Regexp.new("#{plural[0,1].downcase}(?i)#{plural[1..-1]}$"), singular[0,1].downcase + singular[1..-1]) + plural(/#{s0.upcase}(?i)#{srest}$/, p0.upcase + prest) + plural(/#{s0.downcase}(?i)#{srest}$/, p0.downcase + prest) + plural(/#{p0.upcase}(?i)#{prest}$/, p0.upcase + prest) + plural(/#{p0.downcase}(?i)#{prest}$/, p0.downcase + prest) + + singular(/#{s0.upcase}(?i)#{srest}$/, s0.upcase + srest) + singular(/#{s0.downcase}(?i)#{srest}$/, s0.downcase + srest) + singular(/#{p0.upcase}(?i)#{prest}$/, s0.upcase + srest) + singular(/#{p0.downcase}(?i)#{prest}$/, s0.downcase + srest) end end diff --git a/activesupport/lib/active_support/locale/en.yml b/activesupport/lib/active_support/locale/en.yml index f4900dc935..a4563ace8f 100644 --- a/activesupport/lib/active_support/locale/en.yml +++ b/activesupport/lib/active_support/locale/en.yml @@ -16,9 +16,9 @@ en: abbr_month_names: [~, Jan, Feb, Mar, Apr, May, Jun, Jul, Aug, Sep, Oct, Nov, Dec] # Used in date_select and datetime_select. order: - - :year - - :month - - :day + - year + - month + - day time: formats: diff --git a/activesupport/lib/active_support/log_subscriber.rb b/activesupport/lib/active_support/log_subscriber.rb index a58afc6b9d..21a04a9152 100644 --- a/activesupport/lib/active_support/log_subscriber.rb +++ b/activesupport/lib/active_support/log_subscriber.rb @@ -53,7 +53,9 @@ module ActiveSupport class << self def logger - @logger ||= Rails.logger if defined?(Rails) + if defined?(Rails) && Rails.respond_to?(:logger) + @logger ||= Rails.logger + end @logger end diff --git a/activesupport/lib/active_support/notifications/instrumenter.rb b/activesupport/lib/active_support/notifications/instrumenter.rb index ab0b162ee0..1ee7ca06bb 100644 --- a/activesupport/lib/active_support/notifications/instrumenter.rb +++ b/activesupport/lib/active_support/notifications/instrumenter.rb @@ -7,7 +7,7 @@ module ActiveSupport attr_reader :id def initialize(notifier) - @id = unique_id + @id = unique_id @notifier = notifier end @@ -15,21 +15,32 @@ module ActiveSupport # and publish it. Notice that events get sent even if an error occurs # in the passed-in block. def instrument(name, payload={}) - @notifier.start(name, @id, payload) + start name, payload begin yield rescue Exception => e payload[:exception] = [e.class.name, e.message] raise e ensure - @notifier.finish(name, @id, payload) + finish name, payload end end + # Send a start notification with +name+ and +payload+. + def start(name, payload) + @notifier.start name, @id, payload + end + + # Send a finish notification with +name+ and +payload+. + def finish(name, payload) + @notifier.finish name, @id, payload + end + private - def unique_id - SecureRandom.hex(10) - end + + def unique_id + SecureRandom.hex(10) + end end class Event diff --git a/activesupport/lib/active_support/railtie.rb b/activesupport/lib/active_support/railtie.rb index 72ac597d99..133aa6a054 100644 --- a/activesupport/lib/active_support/railtie.rb +++ b/activesupport/lib/active_support/railtie.rb @@ -13,6 +13,20 @@ module ActiveSupport end end + # Sets the default value for Time.zone + # If assigned value cannot be matched to a TimeZone, an exception will be raised. + initializer "active_support.initialize_time_zone" do |app| + require 'active_support/core_ext/time/zones' + zone_default = Time.find_zone!(app.config.time_zone) + + unless zone_default + 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 + + Time.zone_default = zone_default + end + # Sets the default week start # If assigned value is not a valid day symbol (e.g. :sunday, :monday, ...), an exception will be raised. initializer "active_support.initialize_beginning_of_week" do |app| @@ -28,21 +42,5 @@ module ActiveSupport ActiveSupport.send(k, v) if ActiveSupport.respond_to? k end end - - # Sets the default value for Time.zone after initialization since the default configuration - # lives in application initializers. - # If assigned value cannot be matched to a TimeZone, an exception will be raised. - config.after_initialize do |app| - require 'active_support/core_ext/time/zones' - zone_default = Time.find_zone!(app.config.time_zone) - - unless zone_default - 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 - - Time.zone_default = zone_default - end - end end diff --git a/activesupport/lib/active_support/testing/isolation.rb b/activesupport/lib/active_support/testing/isolation.rb index aa87598926..e4f8959e7a 100644 --- a/activesupport/lib/active_support/testing/isolation.rb +++ b/activesupport/lib/active_support/testing/isolation.rb @@ -43,32 +43,36 @@ module ActiveSupport module Isolation require 'thread' - class ParallelEach - include Enumerable - - # default to 2 cores - CORES = (ENV['TEST_CORES'] || 2).to_i - - def initialize list - @list = list - @queue = SizedQueue.new CORES - end + # Recent versions of MiniTest (such as the one shipped with Ruby 2.0) already define + # a ParallelEach class. + unless defined? ParallelEach + class ParallelEach + include Enumerable + + # default to 2 cores + CORES = (ENV['TEST_CORES'] || 2).to_i + + def initialize list + @list = list + @queue = SizedQueue.new CORES + end - def grep pattern - self.class.new super - end + def grep pattern + self.class.new super + end - def each - threads = CORES.times.map { - Thread.new { - while job = @queue.pop - yield job - end + def each + threads = CORES.times.map { + Thread.new { + while job = @queue.pop + yield job + end + } } - } - @list.each { |i| @queue << i } - CORES.times { @queue << nil } - threads.each(&:join) + @list.each { |i| @queue << i } + CORES.times { @queue << nil } + threads.each(&:join) + end end end @@ -84,10 +88,14 @@ module ActiveSupport !ENV["NO_FORK"] && ((RbConfig::CONFIG['host_os'] !~ /mswin|mingw/) && (RUBY_PLATFORM !~ /java/)) end + @@class_setup_mutex = Mutex.new + 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) - @@ran_class_setup = true + @@class_setup_mutex.synchronize do + unless defined?(@@ran_class_setup) || ENV['ISOLATION_TEST'] + self.class.setup if self.class.respond_to?(:setup) + @@ran_class_setup = true + end end end diff --git a/activesupport/lib/active_support/testing/performance.rb b/activesupport/lib/active_support/testing/performance.rb deleted file mode 100644 index 7102ffe2ed..0000000000 --- a/activesupport/lib/active_support/testing/performance.rb +++ /dev/null @@ -1,271 +0,0 @@ -require 'fileutils' -require 'active_support/concern' -require 'active_support/core_ext/class/delegating_attributes' -require 'active_support/core_ext/string/inflections' -require 'active_support/core_ext/module/delegation' -require 'active_support/number_helper' - -module ActiveSupport - module Testing - module Performance - extend ActiveSupport::Concern - - included do - superclass_delegating_accessor :profile_options - self.profile_options = {} - end - - # each implementation should define metrics and freeze the defaults - DEFAULTS = - if ARGV.include?('--benchmark') # HAX for rake test - { :runs => 4, - :output => 'tmp/performance', - :benchmark => true } - else - { :runs => 1, - :output => 'tmp/performance', - :benchmark => false } - end - - def full_profile_options - DEFAULTS.merge(profile_options) - end - - def full_test_name - "#{self.class.name}##{method_name}" - end - - 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 - end - end - - 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 - teardown - run_callbacks :teardown - rescue Exception => e - result = @runner.puke(self.class, method_name, e) - end - end - result - end - - protected - # overridden by each implementation. - def run_gc; end - - def run_warmup - run_gc - - time = Metrics::Time.new - run_test(time, :benchmark) - puts "%s (%s warmup)" % [full_test_name, time.format(time.total)] - - run_gc - end - - def run_profile(metric) - klass = full_profile_options[:benchmark] ? Benchmarker : Profiler - performer = klass.new(self, metric) - - performer.run - puts performer.report - performer.record - end - - class Performer - delegate :run_test, :full_profile_options, :full_test_name, :to => :@harness - - def initialize(harness, metric) - @harness, @metric, @supported = harness, metric, false - end - - def report - if @supported - rate = @total / full_profile_options[:runs] - '%20s: %s' % [@metric.name, @metric.format(rate)] - else - '%20s: unsupported' % @metric.name - end - end - - protected - def output_filename - "#{full_profile_options[:output]}/#{full_test_name}_#{@metric.name}" - end - end - - # overridden by each implementation. - class Profiler < Performer - def time_with_block - before = Time.now - yield - Time.now - before - end - - def run; end - def record; end - end - - class Benchmarker < Performer - def initialize(*args) - super - @supported = @metric.respond_to?('measure') - end - - def run - return unless @supported - - full_profile_options[:runs].to_i.times { run_test(@metric, :benchmark) } - @total = @metric.total - end - - def record - avg = @metric.total / full_profile_options[:runs].to_i - now = Time.now.utc.xmlschema - with_output_file do |file| - file.puts "#{avg},#{now},#{environment}" - end - end - - def environment - @env ||= [].tap do |env| - env << "#{$1}.#{$2}" if File.directory?('.git') && `git branch -v` =~ /^\* (\S+)\s+(\S+)/ - env << rails_version if defined?(Rails::VERSION::STRING) - env << "#{RUBY_ENGINE}-#{RUBY_VERSION}.#{RUBY_PATCHLEVEL}" - env << RUBY_PLATFORM - end.join(',') - end - - protected - if defined?(Rails::VERSION::STRING) - HEADER = 'measurement,created_at,app,rails,ruby,platform' - else - HEADER = 'measurement,created_at,app,ruby,platform' - end - - def with_output_file - fname = output_filename - - if new = !File.exist?(fname) - FileUtils.mkdir_p(File.dirname(fname)) - end - - File.open(fname, 'ab') do |file| - file.puts(HEADER) if new - yield file - end - end - - def output_filename - "#{super}.csv" - end - - def rails_version - "rails-#{Rails::VERSION::STRING}#{rails_branch}" - end - - def rails_branch - if File.directory?('vendor/rails/.git') - Dir.chdir('vendor/rails') do - ".#{$1}.#{$2}" if `git branch -v` =~ /^\* (\S+)\s+(\S+)/ - end - end - end - end - - module Metrics - def self.[](name) - const_get(name.to_s.camelize) - rescue NameError - nil - end - - class Base - include ActiveSupport::NumberHelper - - attr_reader :total - - def initialize - @total = 0 - end - - def name - @name ||= self.class.name.demodulize.underscore - end - - def benchmark - with_gc_stats do - before = measure - yield - @total += (measure - before) - end - end - - # overridden by each implementation. - def profile; end - - protected - # overridden by each implementation. - def with_gc_stats; end - end - - class Time < Base - def measure - ::Time.now.to_f - end - - def format(measurement) - if measurement < 1 - '%d ms' % (measurement * 1000) - else - '%.2f sec' % measurement - end - end - end - - class Amount < Base - def format(measurement) - number_to_delimited(measurement.floor) - end - end - - class DigitalInformationUnit < Base - def format(measurement) - number_to_human_size(measurement, :precision => 2) - end - end - - # each implementation provides its own metrics like ProcessTime, Memory or GcRuns - end - end - end -end - -case RUBY_ENGINE - when 'ruby' then require 'active_support/testing/performance/ruby' - when 'rbx' then require 'active_support/testing/performance/rubinius' - when 'jruby' then require 'active_support/testing/performance/jruby' - else - $stderr.puts 'Your ruby interpreter is not supported for benchmarking.' - exit -end diff --git a/activesupport/lib/active_support/testing/performance/jruby.rb b/activesupport/lib/active_support/testing/performance/jruby.rb deleted file mode 100644 index 34e3f9f45f..0000000000 --- a/activesupport/lib/active_support/testing/performance/jruby.rb +++ /dev/null @@ -1,115 +0,0 @@ -require 'jruby/profiler' -require 'java' -java_import java.lang.management.ManagementFactory - -module ActiveSupport - module Testing - module Performance - DEFAULTS.merge!( - if ARGV.include?('--benchmark') - {:metrics => [:wall_time, :user_time, :memory, :gc_runs, :gc_time]} - else - { :metrics => [:wall_time], - :formats => [:flat, :graph] } - end).freeze - - protected - def run_gc - ManagementFactory.memory_mx_bean.gc - end - - class Profiler < Performer - def initialize(*args) - super - @supported = @metric.is_a?(Metrics::WallTime) - end - - def run - return unless @supported - - @total = time_with_block do - @data = JRuby::Profiler.profile do - full_profile_options[:runs].to_i.times { run_test(@metric, :profile) } - end - end - end - - def record - return unless @supported - - klasses = full_profile_options[:formats].map { |f| JRuby::Profiler.const_get("#{f.to_s.camelize}ProfilePrinter") }.compact - - klasses.each do |klass| - fname = output_filename(klass) - FileUtils.mkdir_p(File.dirname(fname)) - File.open(fname, 'wb') do |file| - klass.new(@data).printProfile(file) - end - end - end - - protected - def output_filename(printer_class) - suffix = - case printer_class.name.demodulize - when 'FlatProfilePrinter'; 'flat.txt' - when 'GraphProfilePrinter'; 'graph.txt' - else printer_class.name.sub(/ProfilePrinter$/, '').underscore - end - - "#{super()}_#{suffix}" - end - end - - module Metrics - class Base - def profile - yield - end - - protected - def with_gc_stats - ManagementFactory.memory_mx_bean.gc - yield - end - end - - class WallTime < Time - def measure - super - end - end - - class CpuTime < Time - def measure - ManagementFactory.thread_mx_bean.get_current_thread_cpu_time / 1000 / 1000 / 1000.0 # seconds - end - end - - class UserTime < Time - def measure - ManagementFactory.thread_mx_bean.get_current_thread_user_time / 1000 / 1000 / 1000.0 # seconds - end - end - - class Memory < DigitalInformationUnit - def measure - ManagementFactory.memory_mx_bean.non_heap_memory_usage.used + ManagementFactory.memory_mx_bean.heap_memory_usage.used - end - end - - class GcRuns < Amount - def measure - ManagementFactory.garbage_collector_mx_beans.inject(0) { |total_runs, current_gc| total_runs += current_gc.collection_count } - end - end - - class GcTime < Time - def measure - ManagementFactory.garbage_collector_mx_beans.inject(0) { |total_time, current_gc| total_time += current_gc.collection_time } / 1000.0 # seconds - end - end - end - end - end -end diff --git a/activesupport/lib/active_support/testing/performance/rubinius.rb b/activesupport/lib/active_support/testing/performance/rubinius.rb deleted file mode 100644 index d9ebfbe352..0000000000 --- a/activesupport/lib/active_support/testing/performance/rubinius.rb +++ /dev/null @@ -1,113 +0,0 @@ -require 'rubinius/agent' - -module ActiveSupport - module Testing - module Performance - DEFAULTS.merge!( - if ARGV.include?('--benchmark') - {:metrics => [:wall_time, :memory, :objects, :gc_runs, :gc_time]} - else - { :metrics => [:wall_time], - :formats => [:flat, :graph] } - end).freeze - - protected - def run_gc - GC.run(true) - end - - class Performer; end - - class Profiler < Performer - def initialize(*args) - super - @supported = @metric.is_a?(Metrics::WallTime) - end - - def run - return unless @supported - - @profiler = Rubinius::Profiler::Instrumenter.new - - @total = time_with_block do - @profiler.profile(false) do - full_profile_options[:runs].to_i.times { run_test(@metric, :profile) } - end - end - end - - def record - return unless @supported - - if(full_profile_options[:formats].include?(:flat)) - create_path_and_open_file(:flat) do |file| - @profiler.show(file) - end - end - - if(full_profile_options[:formats].include?(:graph)) - create_path_and_open_file(:graph) do |file| - @profiler.show(file) - end - end - end - - protected - def create_path_and_open_file(printer_name) - fname = "#{output_filename}_#{printer_name}.txt" - FileUtils.mkdir_p(File.dirname(fname)) - File.open(fname, 'wb') do |file| - yield(file) - end - end - end - - module Metrics - class Base - attr_reader :loopback - - def profile - yield - end - - protected - def with_gc_stats - @loopback = Rubinius::Agent.loopback - GC.run(true) - yield - end - end - - class WallTime < Time - def measure - super - end - end - - class Memory < DigitalInformationUnit - def measure - loopback.get("system.memory.counter.bytes").last - end - end - - class Objects < Amount - def measure - loopback.get("system.memory.counter.objects").last - end - end - - class GcRuns < Amount - def measure - loopback.get("system.gc.full.count").last + loopback.get("system.gc.young.count").last - end - end - - class GcTime < Time - def measure - (loopback.get("system.gc.full.wallclock").last + loopback.get("system.gc.young.wallclock").last) / 1000.0 - end - end - end - end - end -end diff --git a/activesupport/lib/active_support/testing/performance/ruby.rb b/activesupport/lib/active_support/testing/performance/ruby.rb deleted file mode 100644 index 7c149df1e4..0000000000 --- a/activesupport/lib/active_support/testing/performance/ruby.rb +++ /dev/null @@ -1,173 +0,0 @@ -begin - require 'ruby-prof' -rescue LoadError - $stderr.puts 'Specify ruby-prof as application\'s dependency in Gemfile to run benchmarks.' - raise -end - -module ActiveSupport - module Testing - module Performance - DEFAULTS.merge!( - if ARGV.include?('--benchmark') - { :metrics => [:wall_time, :memory, :objects, :gc_runs, :gc_time] } - else - { :min_percent => 0.01, - :metrics => [:process_time, :memory, :objects], - :formats => [:flat, :graph_html, :call_tree, :call_stack] } - end).freeze - - protected - remove_method :run_gc - def run_gc - GC.start - end - - class Profiler < Performer - def initialize(*args) - super - @supported = @metric.measure_mode rescue false - end - - remove_method :run - def run - return unless @supported - - RubyProf.measure_mode = @metric.measure_mode - RubyProf.start - RubyProf.pause - full_profile_options[:runs].to_i.times { run_test(@metric, :profile) } - @data = RubyProf.stop - @total = @data.threads.sum(0) { |thread| thread.methods.max.total_time } - end - - remove_method :record - def record - return unless @supported - - klasses = full_profile_options[:formats].map { |f| RubyProf.const_get("#{f.to_s.camelize}Printer") }.compact - - klasses.each do |klass| - fname = output_filename(klass) - FileUtils.mkdir_p(File.dirname(fname)) - File.open(fname, 'wb') do |file| - klass.new(@data).print(file, full_profile_options.slice(:min_percent)) - end - end - end - - protected - def output_filename(printer_class) - suffix = - case printer_class.name.demodulize - when 'FlatPrinter'; 'flat.txt' - when 'FlatPrinterWithLineNumbers'; 'flat_line_numbers.txt' - when 'GraphPrinter'; 'graph.txt' - when 'GraphHtmlPrinter'; 'graph.html' - when 'GraphYamlPrinter'; 'graph.yml' - when 'CallTreePrinter'; 'tree.txt' - when 'CallStackPrinter'; 'stack.html' - when 'DotPrinter'; 'graph.dot' - else printer_class.name.sub(/Printer$/, '').underscore - end - - "#{super()}_#{suffix}" - end - end - - module Metrics - class Base - def measure_mode - self.class::Mode - end - - remove_method :profile - def profile - RubyProf.resume - yield - ensure - RubyProf.pause - end - - protected - remove_method :with_gc_stats - def with_gc_stats - GC::Profiler.enable - GC.start - yield - ensure - GC::Profiler.disable - end - end - - class ProcessTime < Time - Mode = RubyProf::PROCESS_TIME if RubyProf.const_defined?(:PROCESS_TIME) - - def measure - RubyProf.measure_process_time - end - end - - class WallTime < Time - Mode = RubyProf::WALL_TIME if RubyProf.const_defined?(:WALL_TIME) - - def measure - RubyProf.measure_wall_time - end - end - - class CpuTime < Time - Mode = RubyProf::CPU_TIME if RubyProf.const_defined?(:CPU_TIME) - - def initialize(*args) - # FIXME: yeah my CPU is 2.33 GHz - RubyProf.cpu_frequency = 2.33e9 unless RubyProf.cpu_frequency > 0 - super - end - - def measure - RubyProf.measure_cpu_time - end - end - - 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 diff --git a/activesupport/lib/active_support/time_with_zone.rb b/activesupport/lib/active_support/time_with_zone.rb index d3741845d2..ff13efa990 100644 --- a/activesupport/lib/active_support/time_with_zone.rb +++ b/activesupport/lib/active_support/time_with_zone.rb @@ -317,9 +317,9 @@ module ActiveSupport end alias_method :tv_sec, :to_i - # A TimeWithZone acts like a Time, so just return +self+. + # Return an instance of Time in the system timezone. def to_time - utc + utc.to_time end def to_datetime |