aboutsummaryrefslogtreecommitdiffstats
path: root/activesupport/lib/active_support
diff options
context:
space:
mode:
Diffstat (limited to 'activesupport/lib/active_support')
-rw-r--r--activesupport/lib/active_support/backtrace_cleaner.rb14
-rw-r--r--activesupport/lib/active_support/base64.rb42
-rw-r--r--activesupport/lib/active_support/basic_object.rb24
-rw-r--r--activesupport/lib/active_support/benchmarkable.rb2
-rw-r--r--activesupport/lib/active_support/buffered_logger.rb138
-rw-r--r--activesupport/lib/active_support/cache.rb161
-rw-r--r--activesupport/lib/active_support/cache/file_store.rb29
-rw-r--r--activesupport/lib/active_support/cache/mem_cache_store.rb7
-rw-r--r--activesupport/lib/active_support/cache/null_store.rb44
-rw-r--r--activesupport/lib/active_support/cache/strategy/local_cache.rb4
-rw-r--r--activesupport/lib/active_support/callbacks.rb306
-rw-r--r--activesupport/lib/active_support/concern.rb10
-rw-r--r--activesupport/lib/active_support/configurable.rb2
-rw-r--r--activesupport/lib/active_support/core_ext.rb1
-rw-r--r--activesupport/lib/active_support/core_ext/array.rb2
-rw-r--r--activesupport/lib/active_support/core_ext/array/access.rb2
-rw-r--r--activesupport/lib/active_support/core_ext/array/conversions.rb100
-rw-r--r--activesupport/lib/active_support/core_ext/array/grouping.rb8
-rw-r--r--activesupport/lib/active_support/core_ext/array/prepend_and_append.rb7
-rw-r--r--activesupport/lib/active_support/core_ext/array/random_access.rb30
-rw-r--r--activesupport/lib/active_support/core_ext/array/uniq_by.rb20
-rw-r--r--activesupport/lib/active_support/core_ext/big_decimal/conversions.rb27
-rw-r--r--activesupport/lib/active_support/core_ext/class/attribute.rb6
-rw-r--r--activesupport/lib/active_support/core_ext/class/attribute_accessors.rb4
-rw-r--r--activesupport/lib/active_support/core_ext/class/delegating_attributes.rb34
-rw-r--r--activesupport/lib/active_support/core_ext/class/subclasses.rb2
-rw-r--r--activesupport/lib/active_support/core_ext/date/calculations.rb144
-rw-r--r--activesupport/lib/active_support/core_ext/date/conversions.rb42
-rw-r--r--activesupport/lib/active_support/core_ext/date/freeze.rb33
-rw-r--r--activesupport/lib/active_support/core_ext/date_time/calculations.rb60
-rw-r--r--activesupport/lib/active_support/core_ext/date_time/conversions.rb37
-rw-r--r--activesupport/lib/active_support/core_ext/date_time/zones.rb8
-rw-r--r--activesupport/lib/active_support/core_ext/enumerable.rb82
-rw-r--r--activesupport/lib/active_support/core_ext/exception.rb2
-rw-r--r--activesupport/lib/active_support/core_ext/file.rb1
-rw-r--r--activesupport/lib/active_support/core_ext/file/atomic.rb19
-rw-r--r--activesupport/lib/active_support/core_ext/file/path.rb5
-rw-r--r--activesupport/lib/active_support/core_ext/float.rb1
-rw-r--r--activesupport/lib/active_support/core_ext/float/rounding.rb19
-rw-r--r--activesupport/lib/active_support/core_ext/hash/conversions.rb36
-rw-r--r--activesupport/lib/active_support/core_ext/hash/deep_dup.rb3
-rw-r--r--activesupport/lib/active_support/core_ext/hash/diff.rb6
-rw-r--r--activesupport/lib/active_support/core_ext/hash/except.rb2
-rw-r--r--activesupport/lib/active_support/core_ext/hash/indifferent_access.rb4
-rw-r--r--activesupport/lib/active_support/core_ext/hash/keys.rb25
-rw-r--r--activesupport/lib/active_support/core_ext/hash/reverse_merge.rb1
-rw-r--r--activesupport/lib/active_support/core_ext/hash/slice.rb14
-rw-r--r--activesupport/lib/active_support/core_ext/integer/inflections.rb14
-rw-r--r--activesupport/lib/active_support/core_ext/integer/time.rb6
-rw-r--r--activesupport/lib/active_support/core_ext/io.rb15
-rw-r--r--activesupport/lib/active_support/core_ext/kernel/debugger.rb4
-rw-r--r--activesupport/lib/active_support/core_ext/kernel/reporting.rb7
-rw-r--r--activesupport/lib/active_support/core_ext/kernel/singleton_class.rb7
-rw-r--r--activesupport/lib/active_support/core_ext/logger.rb7
-rw-r--r--activesupport/lib/active_support/core_ext/module.rb3
-rw-r--r--activesupport/lib/active_support/core_ext/module/aliasing.rb15
-rw-r--r--activesupport/lib/active_support/core_ext/module/anonymous.rb8
-rw-r--r--activesupport/lib/active_support/core_ext/module/attr_internal.rb1
-rw-r--r--activesupport/lib/active_support/core_ext/module/attribute_accessors.rb2
-rw-r--r--activesupport/lib/active_support/core_ext/module/delegation.rb101
-rw-r--r--activesupport/lib/active_support/core_ext/module/deprecation.rb2
-rw-r--r--activesupport/lib/active_support/core_ext/module/introspection.rb42
-rw-r--r--activesupport/lib/active_support/core_ext/module/method_names.rb14
-rw-r--r--activesupport/lib/active_support/core_ext/module/qualified_const.rb52
-rw-r--r--activesupport/lib/active_support/core_ext/module/reachable.rb4
-rw-r--r--activesupport/lib/active_support/core_ext/module/remove_method.rb6
-rw-r--r--activesupport/lib/active_support/core_ext/module/synchronization.rb43
-rw-r--r--activesupport/lib/active_support/core_ext/numeric/time.rb6
-rw-r--r--activesupport/lib/active_support/core_ext/object/blank.rb21
-rw-r--r--activesupport/lib/active_support/core_ext/object/duplicable.rb13
-rw-r--r--activesupport/lib/active_support/core_ext/object/inclusion.rb28
-rw-r--r--activesupport/lib/active_support/core_ext/object/instance_variables.rb10
-rw-r--r--activesupport/lib/active_support/core_ext/object/to_json.rb4
-rw-r--r--activesupport/lib/active_support/core_ext/object/to_query.rb2
-rw-r--r--activesupport/lib/active_support/core_ext/object/try.rb2
-rw-r--r--activesupport/lib/active_support/core_ext/object/with_options.rb2
-rw-r--r--activesupport/lib/active_support/core_ext/proc.rb3
-rw-r--r--activesupport/lib/active_support/core_ext/process.rb1
-rw-r--r--activesupport/lib/active_support/core_ext/process/daemon.rb23
-rw-r--r--activesupport/lib/active_support/core_ext/range.rb1
-rw-r--r--activesupport/lib/active_support/core_ext/range/blockless_step.rb26
-rw-r--r--activesupport/lib/active_support/core_ext/range/conversions.rb2
-rw-r--r--activesupport/lib/active_support/core_ext/range/cover.rb3
-rw-r--r--activesupport/lib/active_support/core_ext/range/include_range.rb8
-rw-r--r--activesupport/lib/active_support/core_ext/range/overlaps.rb2
-rw-r--r--activesupport/lib/active_support/core_ext/rexml.rb46
-rw-r--r--activesupport/lib/active_support/core_ext/string.rb1
-rw-r--r--activesupport/lib/active_support/core_ext/string/access.rb114
-rw-r--r--activesupport/lib/active_support/core_ext/string/conversions.rb62
-rw-r--r--activesupport/lib/active_support/core_ext/string/encoding.rb15
-rw-r--r--activesupport/lib/active_support/core_ext/string/filters.rb30
-rw-r--r--activesupport/lib/active_support/core_ext/string/inflections.rb125
-rw-r--r--activesupport/lib/active_support/core_ext/string/inquiry.rb2
-rw-r--r--activesupport/lib/active_support/core_ext/string/multibyte.rb110
-rw-r--r--activesupport/lib/active_support/core_ext/string/output_safety.rb95
-rw-r--r--activesupport/lib/active_support/core_ext/time/calculations.rb167
-rw-r--r--activesupport/lib/active_support/core_ext/time/conversions.rb29
-rw-r--r--activesupport/lib/active_support/core_ext/time/marshal.rb27
-rw-r--r--activesupport/lib/active_support/core_ext/time/publicize_conversion_methods.rb10
-rw-r--r--activesupport/lib/active_support/core_ext/time/zones.rb29
-rw-r--r--activesupport/lib/active_support/core_ext/uri.rb30
-rw-r--r--activesupport/lib/active_support/dependencies.rb123
-rw-r--r--activesupport/lib/active_support/dependencies/autoload.rb11
-rw-r--r--activesupport/lib/active_support/deprecation.rb1
-rw-r--r--activesupport/lib/active_support/deprecation/behaviors.rb19
-rw-r--r--activesupport/lib/active_support/deprecation/method_wrappers.rb5
-rw-r--r--activesupport/lib/active_support/duration.rb1
-rw-r--r--activesupport/lib/active_support/file_update_checker.rb132
-rw-r--r--activesupport/lib/active_support/gzip.rb3
-rw-r--r--activesupport/lib/active_support/hash_with_indifferent_access.rb19
-rw-r--r--activesupport/lib/active_support/i18n_railtie.rb16
-rw-r--r--activesupport/lib/active_support/inflections.rb24
-rw-r--r--activesupport/lib/active_support/inflector/inflections.rb15
-rw-r--r--activesupport/lib/active_support/inflector/methods.rb225
-rw-r--r--activesupport/lib/active_support/json/decoding.rb6
-rw-r--r--activesupport/lib/active_support/json/encoding.rb33
-rw-r--r--activesupport/lib/active_support/lazy_load_hooks.rb50
-rw-r--r--activesupport/lib/active_support/log_subscriber.rb12
-rw-r--r--activesupport/lib/active_support/log_subscriber/test_helper.rb8
-rw-r--r--activesupport/lib/active_support/logger.rb53
-rw-r--r--activesupport/lib/active_support/memoizable.rb116
-rw-r--r--activesupport/lib/active_support/message_encryptor.rb77
-rw-r--r--activesupport/lib/active_support/message_verifier.rb16
-rw-r--r--activesupport/lib/active_support/multibyte.rb25
-rw-r--r--activesupport/lib/active_support/multibyte/chars.rb344
-rw-r--r--activesupport/lib/active_support/multibyte/exceptions.rb8
-rw-r--r--activesupport/lib/active_support/multibyte/unicode.rb81
-rw-r--r--activesupport/lib/active_support/multibyte/utils.rb60
-rw-r--r--activesupport/lib/active_support/notifications.rb139
-rw-r--r--activesupport/lib/active_support/notifications/fanout.rb103
-rw-r--r--activesupport/lib/active_support/notifications/instrumenter.rb8
-rw-r--r--activesupport/lib/active_support/ordered_hash.rb189
-rw-r--r--activesupport/lib/active_support/ordered_options.rb9
-rw-r--r--activesupport/lib/active_support/railtie.rb34
-rw-r--r--activesupport/lib/active_support/rescuable.rb6
-rw-r--r--activesupport/lib/active_support/ruby/shim.rb9
-rw-r--r--activesupport/lib/active_support/string_inquirer.rb4
-rw-r--r--activesupport/lib/active_support/tagged_logging.rb58
-rw-r--r--activesupport/lib/active_support/test_case.rb45
-rw-r--r--activesupport/lib/active_support/testing/assertions.rb5
-rw-r--r--activesupport/lib/active_support/testing/deprecation.rb19
-rw-r--r--activesupport/lib/active_support/testing/isolation.rb58
-rw-r--r--activesupport/lib/active_support/testing/pending.rb52
-rw-r--r--activesupport/lib/active_support/testing/performance.rb88
-rw-r--r--activesupport/lib/active_support/testing/performance/jruby.rb2
-rw-r--r--activesupport/lib/active_support/testing/performance/ruby.rb36
-rw-r--r--activesupport/lib/active_support/testing/performance/ruby/mri.rb57
-rw-r--r--activesupport/lib/active_support/testing/performance/ruby/yarv.rb57
-rw-r--r--activesupport/lib/active_support/testing/setup_and_teardown.rb88
-rw-r--r--activesupport/lib/active_support/time.rb2
-rw-r--r--activesupport/lib/active_support/time/autoload.rb5
-rw-r--r--activesupport/lib/active_support/time_with_zone.rb17
-rw-r--r--activesupport/lib/active_support/values/time_zone.rb95
-rw-r--r--activesupport/lib/active_support/values/unicode_tables.datbin813343 -> 904408 bytes
-rw-r--r--activesupport/lib/active_support/version.rb4
-rw-r--r--activesupport/lib/active_support/whiny_nil.rb60
-rw-r--r--activesupport/lib/active_support/xml_mini.rb9
-rw-r--r--activesupport/lib/active_support/xml_mini/jdom.rb9
-rw-r--r--activesupport/lib/active_support/xml_mini/libxml.rb1
-rw-r--r--activesupport/lib/active_support/xml_mini/libxmlsax.rb3
-rw-r--r--activesupport/lib/active_support/xml_mini/nokogiri.rb1
-rw-r--r--activesupport/lib/active_support/xml_mini/nokogirisax.rb3
-rw-r--r--activesupport/lib/active_support/xml_mini/rexml.rb1
163 files changed, 2387 insertions, 3344 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/base64.rb b/activesupport/lib/active_support/base64.rb
deleted file mode 100644
index 35014cb3d5..0000000000
--- a/activesupport/lib/active_support/base64.rb
+++ /dev/null
@@ -1,42 +0,0 @@
-begin
- require 'base64'
-rescue LoadError
-end
-
-module ActiveSupport
- if defined? ::Base64
- Base64 = ::Base64
- else
- # Base64 provides utility methods for encoding and de-coding binary data
- # using a base 64 representation. A base 64 representation of binary data
- # consists entirely of printable US-ASCII characters. The Base64 module
- # is included in Ruby 1.8, but has been removed in Ruby 1.9.
- module Base64
- # Encodes a string to its base 64 representation. Each 60 characters of
- # output is separated by a newline character.
- #
- # ActiveSupport::Base64.encode64("Original unencoded string")
- # # => "T3JpZ2luYWwgdW5lbmNvZGVkIHN0cmluZw==\n"
- def self.encode64(data)
- [data].pack("m")
- end
-
- # Decodes a base 64 encoded string to its original representation.
- #
- # ActiveSupport::Base64.decode64("T3JpZ2luYWwgdW5lbmNvZGVkIHN0cmluZw==")
- # # => "Original unencoded string"
- def self.decode64(data)
- data.unpack("m").first
- end
- end
- end
-
- # Encodes the value as base64 without the newline breaks. This makes the base64 encoding readily usable as URL parameters
- # or memcache keys without further processing.
- #
- # ActiveSupport::Base64.encode64s("Original unencoded string")
- # # => "T3JpZ2luYWwgdW5lbmNvZGVkIHN0cmluZw=="
- def Base64.encode64s(value)
- encode64(value).gsub(/\n/, '')
- end
-end
diff --git a/activesupport/lib/active_support/basic_object.rb b/activesupport/lib/active_support/basic_object.rb
index 3b5277c205..6ccb0cd525 100644
--- a/activesupport/lib/active_support/basic_object.rb
+++ b/activesupport/lib/active_support/basic_object.rb
@@ -1,21 +1,13 @@
module ActiveSupport
- if defined? ::BasicObject
- # A class with no predefined methods that behaves similarly to Builder's
- # BlankSlate. Used for proxy classes.
- class BasicObject < ::BasicObject
- undef_method :==
- undef_method :equal?
+ # A class with no predefined methods that behaves similarly to Builder's
+ # BlankSlate. Used for proxy classes.
+ class BasicObject < ::BasicObject
+ undef_method :==
+ undef_method :equal?
- # Let ActiveSupport::BasicObject at least raise exceptions.
- def raise(*args)
- ::Object.send(:raise, *args)
- end
- end
- else
- class BasicObject #:nodoc:
- instance_methods.each do |m|
- undef_method(m) if m.to_s !~ /(?:^__|^nil\?$|^send$|^object_id$)/
- end
+ # Let ActiveSupport::BasicObject at least raise exceptions.
+ def raise(*args)
+ ::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 26412cd7f4..0595446189 100644
--- a/activesupport/lib/active_support/buffered_logger.rb
+++ b/activesupport/lib/active_support/buffered_logger.rb
@@ -1,137 +1,7 @@
-require 'thread'
-require 'active_support/core_ext/class/attribute_accessors'
+require 'active_support/deprecation'
+require 'active_support/logger'
module ActiveSupport
- # Inspired by the buffered logger idea by Ezra
- class BufferedLogger
- module Severity
- DEBUG = 0
- INFO = 1
- WARN = 2
- ERROR = 3
- FATAL = 4
- UNKNOWN = 5
- end
- include Severity
-
- MAX_BUFFER_SIZE = 1000
-
- ##
- # :singleton-method:
- # Set to false to disable the silencer
- cattr_accessor :silencer
- self.silencer = true
-
- # Silences the logger for the duration of the block.
- def silence(temporary_level = ERROR)
- if silencer
- begin
- old_logger_level, self.level = level, temporary_level
- yield self
- ensure
- self.level = old_logger_level
- end
- else
- yield self
- end
- end
-
- attr_accessor :level
- attr_reader :auto_flushing
-
- def initialize(log, level = DEBUG)
- @level = level
- @buffer = Hash.new { |h,k| h[k] = [] }
- @auto_flushing = 1
- @guard = Mutex.new
-
- if log.respond_to?(:write)
- @log = log
- elsif File.exist?(log)
- @log = open_log(log, (File::WRONLY | File::APPEND))
- else
- FileUtils.mkdir_p(File.dirname(log))
- @log = open_log(log, (File::WRONLY | File::APPEND | File::CREAT))
- end
- end
-
- def open_log(log, mode)
- open(log, mode).tap do |open_log|
- open_log.set_encoding(Encoding::BINARY) if open_log.respond_to?(:set_encoding)
- open_log.sync = true
- end
- end
-
- def add(severity, message = nil, progname = nil, &block)
- return if @level > severity
- message = (message || (block && block.call) || progname).to_s
- # If a newline is necessary then create a new message ending with a newline.
- # Ensures that the original message is not mutated.
- message = "#{message}\n" unless message[-1] == ?\n
- buffer << message
- auto_flush
- message
- end
-
- # Dynamically add methods such as:
- # def info
- # def warn
- # def debug
- Severity.constants.each do |severity|
- class_eval <<-EOT, __FILE__, __LINE__ + 1
- def #{severity.downcase}(message = nil, progname = nil, &block) # def debug(message = nil, progname = nil, &block)
- add(#{severity}, message, progname, &block) # add(DEBUG, message, progname, &block)
- end # end
-
- def #{severity.downcase}? # def debug?
- #{severity} >= @level # DEBUG >= @level
- end # end
- EOT
- end
-
- # Set the auto-flush period. Set to true to flush after every log message,
- # to an integer to flush every N messages, or to false, nil, or zero to
- # never auto-flush. If you turn auto-flushing off, be sure to regularly
- # flush the log yourself -- it will eat up memory until you do.
- def auto_flushing=(period)
- @auto_flushing =
- case period
- when true; 1
- when false, nil, 0; MAX_BUFFER_SIZE
- when Integer; period
- else raise ArgumentError, "Unrecognized auto_flushing period: #{period.inspect}"
- end
- end
-
- def flush
- @guard.synchronize do
- buffer.each do |content|
- @log.write(content)
- end
-
- # Important to do this even if buffer was empty or else @buffer will
- # accumulate empty arrays for each request where nothing was logged.
- clear_buffer
- end
- end
-
- def close
- flush
- @log.close if @log.respond_to?(:close)
- @log = nil
- end
-
- protected
- def auto_flush
- flush if buffer.size >= @auto_flushing
- end
-
- def buffer
- @buffer[Thread.current]
- end
-
- def clear_buffer
- @buffer.delete(Thread.current)
- end
- end
+ BufferedLogger = ActiveSupport::Deprecation::DeprecatedConstantProxy.new(
+ 'BufferedLogger', '::ActiveSupport::Logger')
end
diff --git a/activesupport/lib/active_support/cache.rb b/activesupport/lib/active_support/cache.rb
index 95d936b32f..b9f196d7a9 100644
--- a/activesupport/lib/active_support/cache.rb
+++ b/activesupport/lib/active_support/cache.rb
@@ -16,8 +16,7 @@ module ActiveSupport
autoload :FileStore, 'active_support/cache/file_store'
autoload :MemoryStore, 'active_support/cache/memory_store'
autoload :MemCacheStore, 'active_support/cache/mem_cache_store'
- autoload :SynchronizedMemoryStore, 'active_support/cache/synchronized_memory_store'
- autoload :CompressedMemCacheStore, 'active_support/cache/compressed_mem_cache_store'
+ autoload :NullStore, 'active_support/cache/null_store'
# These options mean something to all cache implementations. Individual cache
# implementations may support additional options.
@@ -27,75 +26,75 @@ module ActiveSupport
autoload :LocalCache, 'active_support/cache/strategy/local_cache'
end
- # Creates a new CacheStore object according to the given options.
- #
- # If no arguments are passed to this method, then a new
- # ActiveSupport::Cache::MemoryStore object will be returned.
- #
- # If you pass a Symbol as the first argument, then a corresponding cache
- # store class under the ActiveSupport::Cache namespace will be created.
- # For example:
- #
- # ActiveSupport::Cache.lookup_store(:memory_store)
- # # => returns a new ActiveSupport::Cache::MemoryStore object
- #
- # ActiveSupport::Cache.lookup_store(:mem_cache_store)
- # # => returns a new ActiveSupport::Cache::MemCacheStore object
- #
- # Any additional arguments will be passed to the corresponding cache store
- # class's constructor:
- #
- # ActiveSupport::Cache.lookup_store(:file_store, "/tmp/cache")
- # # => same as: ActiveSupport::Cache::FileStore.new("/tmp/cache")
- #
- # If the first argument is not a Symbol, then it will simply be returned:
- #
- # ActiveSupport::Cache.lookup_store(MyOwnCacheStore.new)
- # # => returns MyOwnCacheStore.new
- def self.lookup_store(*store_option)
- store, *parameters = *Array.wrap(store_option).flatten
-
- case store
- when Symbol
- store_class_name = store.to_s.camelize
- store_class =
- begin
- require "active_support/cache/#{store}"
- rescue LoadError => e
- raise "Could not find cache store adapter for #{store} (#{e})"
- else
- ActiveSupport::Cache.const_get(store_class_name)
- end
- store_class.new(*parameters)
- when nil
- ActiveSupport::Cache::MemoryStore.new
- else
- store
+ class << self
+ # Creates a new CacheStore object according to the given options.
+ #
+ # If no arguments are passed to this method, then a new
+ # ActiveSupport::Cache::MemoryStore object will be returned.
+ #
+ # If you pass a Symbol as the first argument, then a corresponding cache
+ # store class under the ActiveSupport::Cache namespace will be created.
+ # For example:
+ #
+ # ActiveSupport::Cache.lookup_store(:memory_store)
+ # # => returns a new ActiveSupport::Cache::MemoryStore object
+ #
+ # ActiveSupport::Cache.lookup_store(:mem_cache_store)
+ # # => returns a new ActiveSupport::Cache::MemCacheStore object
+ #
+ # Any additional arguments will be passed to the corresponding cache store
+ # class's constructor:
+ #
+ # ActiveSupport::Cache.lookup_store(:file_store, "/tmp/cache")
+ # # => same as: ActiveSupport::Cache::FileStore.new("/tmp/cache")
+ #
+ # If the first argument is not a Symbol, then it will simply be returned:
+ #
+ # ActiveSupport::Cache.lookup_store(MyOwnCacheStore.new)
+ # # => returns MyOwnCacheStore.new
+ def lookup_store(*store_option)
+ store, *parameters = *Array.wrap(store_option).flatten
+
+ case store
+ when Symbol
+ store_class_name = store.to_s.camelize
+ store_class =
+ begin
+ require "active_support/cache/#{store}"
+ rescue LoadError => e
+ raise "Could not find cache store adapter for #{store} (#{e})"
+ else
+ ActiveSupport::Cache.const_get(store_class_name)
+ end
+ store_class.new(*parameters)
+ when nil
+ ActiveSupport::Cache::MemoryStore.new
+ else
+ store
+ end
end
- end
- def self.expand_cache_key(key, namespace = nil)
- expanded_cache_key = namespace ? "#{namespace}/" : ""
+ def expand_cache_key(key, namespace = nil)
+ expanded_cache_key = namespace ? "#{namespace}/" : ""
+
+ if prefix = ENV["RAILS_CACHE_ID"] || ENV["RAILS_APP_VERSION"]
+ expanded_cache_key << "#{prefix}/"
+ end
- prefix = ENV["RAILS_CACHE_ID"] || ENV["RAILS_APP_VERSION"]
- if prefix
- expanded_cache_key << "#{prefix}/"
+ expanded_cache_key << retrieve_cache_key(key)
+ expanded_cache_key
end
- expanded_cache_key <<
- if key.respond_to?(:cache_key)
- key.cache_key
- elsif key.is_a?(Array)
- if key.size > 1
- key.collect { |element| expand_cache_key(element) }.to_param
- else
- key.first.to_param
- end
- elsif key
- key.to_param
- end.to_s
+ private
- expanded_cache_key
+ def retrieve_cache_key(key)
+ case
+ when key.respond_to?(:cache_key) then key.cache_key
+ 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
end
# An abstract cache store class. There are multiple cache store
@@ -141,7 +140,7 @@ module ActiveSupport
# large enough to warrant compression. To turn on compression either pass
# <tt>:compress => true</tt> in the initializer or as an option to +fetch+
# or +write+. To specify the threshold at which to compress values, set the
- # <tt>:compress_threshold</tt> option. The default threshold is 32K.
+ # <tt>:compress_threshold</tt> option. The default threshold is 16K.
class Store
cattr_accessor :logger, :instance_writer => true
@@ -151,7 +150,7 @@ module ActiveSupport
# Create a new cache. The options will be passed to any write method calls except
# for :namespace which can be used to set the global namespace for the cache.
- def initialize (options = nil)
+ def initialize(options = nil)
@options = options ? options.dup : {}
end
@@ -232,7 +231,7 @@ module ActiveSupport
# <tt>:race_condition_ttl</tt> does not play any role.
#
# # Set all values to expire after one minute.
- # cache = ActiveSupport::Cache::MemoryCache.new(:expires_in => 1.minute)
+ # cache = ActiveSupport::Cache::MemoryStore.new(:expires_in => 1.minute)
#
# cache.write("foo", "original value")
# val_1 = nil
@@ -384,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
@@ -540,11 +535,11 @@ module ActiveSupport
# Create an entry with internal attributes set. This method is intended to be
# used by implementations that store cache entries in a native format instead
# of as serialized Ruby objects.
- def create (raw_value, created_at, options = {})
+ def create(raw_value, created_at, options = {})
entry = new(nil)
entry.instance_variable_set(:@value, raw_value)
entry.instance_variable_set(:@created_at, created_at.to_f)
- entry.instance_variable_set(:@compressed, !!options[:compressed])
+ entry.instance_variable_set(:@compressed, options[:compressed])
entry.instance_variable_set(:@expires_in, options[:expires_in])
entry
end
@@ -561,7 +556,7 @@ module ActiveSupport
@value = nil
else
@value = Marshal.dump(value)
- if should_compress?(value, options)
+ if should_compress?(@value, options)
@value = Zlib::Deflate.deflate(@value)
@compressed = true
end
@@ -575,6 +570,9 @@ module ActiveSupport
# Get the value stored in the cache.
def value
+ # If the original value was exactly false @value is still true because
+ # it is marshalled and eventually compressed. Both operations yield
+ # strings.
if @value
Marshal.load(compressed? ? Zlib::Inflate.inflate(@value) : @value)
end
@@ -615,13 +613,10 @@ module ActiveSupport
end
private
- def should_compress?(value, options)
- if options[:compress] && value
- unless value.is_a?(Numeric)
- compress_threshold = options[:compress_threshold] || DEFAULT_COMPRESS_LIMIT
- serialized_value = value.is_a?(String) ? value : Marshal.dump(value)
- return true if serialized_value.size >= compress_threshold
- end
+ def should_compress?(serialized_value, options)
+ if options[:compress]
+ compress_threshold = options[:compress_threshold] || DEFAULT_COMPRESS_LIMIT
+ return true if serialized_value.size >= compress_threshold
end
false
end
diff --git a/activesupport/lib/active_support/cache/file_store.rb b/activesupport/lib/active_support/cache/file_store.rb
index f7c01948b4..8e6a3bc5a8 100644
--- a/activesupport/lib/active_support/cache/file_store.rb
+++ b/activesupport/lib/active_support/cache/file_store.rb
@@ -8,11 +8,13 @@ module ActiveSupport
# A cache store implementation which stores everything on the filesystem.
#
# FileStore implements the Strategy::LocalCache strategy which implements
- # an in memory cache inside of a block.
+ # an in-memory cache inside of a block.
class FileStore < Store
attr_reader :cache_path
DIR_FORMATTER = "%03X"
+ 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)
super(options)
@@ -21,7 +23,7 @@ module ActiveSupport
end
def clear(options = nil)
- root_dirs = Dir.entries(cache_path).reject{|f| f.in?(['.', '..'])}
+ root_dirs = Dir.entries(cache_path).reject{|f| f.in?(EXCLUDED_DIRS)}
FileUtils.rm_r(root_dirs.collect{|f| File.join(cache_path, f)})
end
@@ -129,28 +131,26 @@ module ActiveSupport
hash, dir_1 = hash.divmod(0x1000)
dir_2 = hash.modulo(0x1000)
fname_paths = []
- # Make sure file name is < 255 characters so it doesn't exceed file system limits.
- if fname.size <= 255
- fname_paths << fname
- else
- while fname.size <= 255
- fname_path << fname[0, 255]
- fname = fname[255, -1]
- end
- end
+
+ # Make sure file name doesn't exceed file system limits.
+ begin
+ fname_paths << fname[0, FILENAME_MAX_SIZE]
+ fname = fname[FILENAME_MAX_SIZE..-1]
+ end until fname.blank?
+
File.join(cache_path, DIR_FORMATTER % dir_1, DIR_FORMATTER % dir_2, *fname_paths)
end
# 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
# Delete empty directories in the cache.
def delete_empty_directories(dir)
return if dir == cache_path
- if Dir.entries(dir).reject{|f| f.in?(['.', '..'])}.empty?
+ if Dir.entries(dir).reject{|f| f.in?(EXCLUDED_DIRS)}.empty?
File.delete(dir) rescue nil
delete_empty_directories(File.dirname(dir))
end
@@ -162,8 +162,9 @@ module ActiveSupport
end
def search_dir(dir, &callback)
+ return if !File.exist?(dir)
Dir.foreach(dir) do |d|
- next if d == "." || d == ".."
+ next if d.in?(EXCLUDED_DIRS)
name = File.join(dir, d)
if File.directory?(name)
search_dir(name, &callback)
diff --git a/activesupport/lib/active_support/cache/mem_cache_store.rb b/activesupport/lib/active_support/cache/mem_cache_store.rb
index e07294178b..2e1ccb72d8 100644
--- a/activesupport/lib/active_support/cache/mem_cache_store.rb
+++ b/activesupport/lib/active_support/cache/mem_cache_store.rb
@@ -6,12 +6,11 @@ rescue LoadError => e
end
require 'digest/md5'
-require 'active_support/core_ext/string/encoding'
module ActiveSupport
module Cache
# A cache store implementation which stores data in Memcached:
- # http://www.danga.com/memcached/
+ # http://memcached.org/
#
# This is currently the most popular cache store for production websites.
#
@@ -21,7 +20,7 @@ module ActiveSupport
# server goes down, then MemCacheStore will ignore it until it comes back up.
#
# MemCacheStore implements the Strategy::LocalCache strategy which implements
- # an in memory cache inside of a block.
+ # an in-memory cache inside of a block.
class MemCacheStore < Store
module Response # :nodoc:
STORED = "STORED\r\n"
@@ -165,7 +164,7 @@ module ActiveSupport
# characters properly.
def escape_key(key)
key = key.to_s.dup
- key = key.force_encoding("BINARY") if key.encoding_aware?
+ key = key.force_encoding("BINARY")
key = key.gsub(ESCAPE_KEY_CHARS){ |match| "%#{match.getbyte(0).to_s(16).upcase}" }
key = "#{key[0, 213]}:md5:#{Digest::MD5.hexdigest(key)}" if key.size > 250
key
diff --git a/activesupport/lib/active_support/cache/null_store.rb b/activesupport/lib/active_support/cache/null_store.rb
new file mode 100644
index 0000000000..4427eaafcd
--- /dev/null
+++ b/activesupport/lib/active_support/cache/null_store.rb
@@ -0,0 +1,44 @@
+module ActiveSupport
+ module Cache
+ # A cache store implementation which doesn't actually store anything. Useful in
+ # development and test environments where you don't want caching turned on but
+ # need to go through the caching interface.
+ #
+ # This cache does implement the local cache strategy, so values will actually
+ # be cached inside blocks that utilize this strategy. See
+ # ActiveSupport::Cache::Strategy::LocalCache for more details.
+ class NullStore < Store
+ def initialize(options = nil)
+ super(options)
+ extend Strategy::LocalCache
+ end
+
+ def clear(options = nil)
+ end
+
+ def cleanup(options = nil)
+ end
+
+ def increment(name, amount = 1, options = nil)
+ end
+
+ def decrement(name, amount = 1, options = nil)
+ end
+
+ def delete_matched(matcher, options = nil)
+ end
+
+ protected
+ def read_entry(key, options) # :nodoc:
+ end
+
+ def write_entry(key, entry, options) # :nodoc:
+ true
+ end
+
+ def delete_entry(key, options) # :nodoc:
+ false
+ end
+ end
+ end
+end
diff --git a/activesupport/lib/active_support/cache/strategy/local_cache.rb b/activesupport/lib/active_support/cache/strategy/local_cache.rb
index 0649a058aa..db5f228a70 100644
--- a/activesupport/lib/active_support/cache/strategy/local_cache.rb
+++ b/activesupport/lib/active_support/cache/strategy/local_cache.rb
@@ -4,9 +4,9 @@ require 'active_support/core_ext/string/inflections'
module ActiveSupport
module Cache
module Strategy
- # Caches that implement LocalCache will be backed by an in memory cache for the
+ # Caches that implement LocalCache will be backed by an in-memory cache for the
# duration of a block. Repeated calls to the cache for the same key will hit the
- # in memory cache for faster access.
+ # in-memory cache for faster access.
module LocalCache
# Simple memory backed cache. This cache is not thread safe and is intended only
# for serving as a temporary memory cache for a single thread.
diff --git a/activesupport/lib/active_support/callbacks.rb b/activesupport/lib/active_support/callbacks.rb
index 656cba625c..cbeba3139a 100644
--- a/activesupport/lib/active_support/callbacks.rb
+++ b/activesupport/lib/active_support/callbacks.rb
@@ -1,6 +1,5 @@
require 'active_support/concern'
require 'active_support/descendants_tracker'
-require 'active_support/core_ext/array/wrap'
require 'active_support/core_ext/class/attribute'
require 'active_support/core_ext/kernel/reporting'
require 'active_support/core_ext/kernel/singleton_class'
@@ -67,8 +66,6 @@ module ActiveSupport
#
# Calls the before and around callbacks in the order they were set, yields
# the block (if given one), and then runs the after callbacks in reverse order.
- # Optionally accepts a key, which will be used to compile an optimized callback
- # method for each key. See +ClassMethods.define_callbacks+ for more information.
#
# If the callback chain was halted, returns +false+. Otherwise returns the result
# of the block, or +true+ if no block is given.
@@ -77,48 +74,54 @@ 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
+
+ # A hook invoked everytime a before callback is halted.
+ # This can be overriden in AS::Callback implementors in order
+ # to provide better debugging/logging.
+ def halted_callback_hook(filter)
end
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
end
def normalize_options!(options)
- options[:if] = Array.wrap(options[:if])
- options[:unless] = Array.wrap(options[:unless])
-
- options[:per_key] ||= {}
- options[:per_key][:if] = Array.wrap(options[:per_key][:if])
- options[:per_key][:unless] = Array.wrap(options[:per_key][:unless])
+ options[:if] = Array(options[:if])
+ options[:unless] = Array(options[:unless])
end
def name
@@ -134,100 +137,46 @@ 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 #{key_options[0]}
- end
- RUBY_EVAL
+ recompile_options!
end
- # This will supply contents for before and around filters, and no
- # contents for after filters (for the forward pass).
- def start(key=nil, object=nil)
- return if key && !object.send("_one_time_conditions_valid_#{@callback_id}?")
-
- # options[0] is the compiled form of supplied conditions
- # options[1] is the "end" for the conditional
- #
+ # Wraps code with filter
+ def apply(code)
case @kind
when :before
- # if condition # before_save :filter_name, :if => :condition
- # filter_name
- # end
- filter = <<-RUBY_EVAL
- unless halted
- # This double assignment is to prevent warnings in 1.9.3. I would
- # remove the `result` variable, but apparently some other
- # generated code is depending on this variable being set sometimes
- # and sometimes not.
+ <<-RUBY_EVAL
+ if !halted && #{@compiled_options}
+ # This double assignment is to prevent warnings in 1.9.3 as
+ # the `result` variable is not always used except if the
+ # terminator code refers to it.
result = result = #{@filter}
halted = (#{chain.config[:terminator]})
- end
- RUBY_EVAL
-
- [@compiled_options[0], filter, @compiled_options[1]].compact.join("\n")
- when :around
- # Compile around filters with conditions into proxy methods
- # that contain the conditions.
- #
- # For `around_save :filter_name, :if => :condition':
- #
- # def _conditional_callback_save_17
- # if condition
- # filter_name do
- # yield self
- # end
- # else
- # yield self
- # end
- # end
- #
- name = "_conditional_callback_#{@kind}_#{next_id}"
- @klass.class_eval <<-RUBY_EVAL, __FILE__, __LINE__ + 1
- def #{name}(halted)
- #{@compiled_options[0] || "if true"} && !halted
- #{@filter} do
- yield self
- end
- else
- yield self
+ if halted
+ halted_callback_hook(#{@raw_filter.inspect.inspect})
end
end
+ #{code}
RUBY_EVAL
- "#{name}(halted) do"
- end
- end
-
- # This will supply contents for around and after filters, but not
- # before filters (for the backward pass).
- def end(key=nil, object=nil)
- return if key && !object.send("_one_time_conditions_valid_#{@callback_id}?")
-
- case @kind
when :after
- # if condition # after_save :filter_name, :if => :condition
- # filter_name
- # end
- [@compiled_options[0], @filter, @compiled_options[1]].compact.join("\n")
+ <<-RUBY_EVAL
+ #{code}
+ if #{!chain.config[:skip_after_callbacks_if_terminated] || "!halted"} && #{@compiled_options}
+ #{@filter}
+ end
+ RUBY_EVAL
when :around
+ name = define_conditional_callback
<<-RUBY_EVAL
+ #{name}(halted) do
+ #{code}
value
end
RUBY_EVAL
@@ -236,23 +185,52 @@ module ActiveSupport
private
+ # Compile around filters with conditions into proxy methods
+ # that contain the conditions.
+ #
+ # For `set_callback :save, :around, :filter_name, :if => :condition':
+ #
+ # def _conditional_callback_save_17
+ # if condition
+ # filter_name do
+ # yield self
+ # end
+ # else
+ # yield self
+ # end
+ # end
+ #
+ def define_conditional_callback
+ name = "_conditional_callback_#{@kind}_#{next_id}"
+ @klass.class_eval <<-RUBY_EVAL, __FILE__, __LINE__ + 1
+ def #{name}(halted)
+ if #{@compiled_options} && !halted
+ #{@filter} do
+ yield self
+ end
+ else
+ yield self
+ end
+ end
+ RUBY_EVAL
+ name
+ end
+
# 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)
- return [] if options[:if].empty? && options[:unless].empty?
-
- conditions = []
+ def recompile_options!
+ conditions = ["true"]
unless options[:if].empty?
- conditions << Array.wrap(_compile_filter(options[:if]))
+ conditions << Array(_compile_filter(options[:if]))
end
unless options[:unless].empty?
- conditions << Array.wrap(_compile_filter(options[:unless])).map {|f| "!#{f}"}
+ conditions << Array(_compile_filter(options[:unless])).map {|f| "!#{f}"}
end
- ["if #{conditions.flatten.join(" && ")}", "end"]
+ @compiled_options = conditions.flatten.join(" && ")
end
# Filters support:
@@ -294,7 +272,7 @@ module ActiveSupport
@klass.send(:define_method, "#{method_name}_object") { filter }
_normalize_legacy_filter(kind, filter)
- scopes = Array.wrap(chain.config[:scope])
+ scopes = Array(chain.config[:scope])
method_to_call = scopes.map{ |s| s.is_a?(Symbol) ? send(s) : s }.join("_")
@klass.class_eval <<-RUBY_EVAL, __FILE__, __LINE__ + 1
@@ -330,83 +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"
- each do |callback|
- method << callback.start(key, object)
- end
-
- 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
-
+ callbacks = "value = yield if block_given? && !halted"
reverse_each do |callback|
- method << callback.end(key, object)
+ 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.compact.join("\n")
+ method.join("\n")
end
+
end
module ClassMethods
- # Generate the internal runner method called by +run_callbacks+.
- def __define_runner(symbol) #:nodoc:
- body = send("_#{symbol}_callbacks").compile
- silence_warnings do
- undef_method "_run_#{symbol}_callbacks" if method_defined?("_run_#{symbol}_callbacks")
- class_eval <<-RUBY_EVAL, __FILE__, __LINE__ + 1
- def _run_#{symbol}_callbacks(key = nil, &blk)
- if key
- name = "_run__\#{self.class.name.hash.abs}__#{symbol}__\#{key.hash.abs}__callbacks"
-
- unless respond_to?(name)
- self.class.__create_keyed_callback(name, :#{symbol}, self, &blk)
- end
-
- send(name, &blk)
- else
- #{body}
- end
- end
- private :_run_#{symbol}_callbacks
- RUBY_EVAL
- end
- end
-
- # This is called the first time a callback is called with a particular
- # key. It creates a new callback method for the key, calculating
- # which callbacks can be omitted because of per_key conditions.
- #
- def __create_keyed_callback(name, kind, object, &blk) #:nodoc:
- @_keyed_callbacks ||= {}
- @_keyed_callbacks[name] ||= begin
- str = send("_#{kind}_callbacks").compile(name, 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
- true
end
+ name
+ end
+
+ def __reset_runner(symbol)
+ name = __callback_runner_name(symbol)
+ undef_method(name) if method_defined?(name)
+ end
+
+ 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
@@ -420,7 +366,7 @@ module ActiveSupport
([self] + ActiveSupport::DescendantsTracker.descendants(self)).reverse.each do |target|
chain = target.send("_#{name}_callbacks")
yield target, chain.dup, type, filters, options
- target.__define_runner(name)
+ target.__reset_runner(name)
end
end
@@ -459,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
@@ -516,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)
@@ -534,12 +457,12 @@ module ActiveSupport
chain = target.send("_#{symbol}_callbacks").dup
callbacks.each { |c| chain.delete(c) }
target.send("_#{symbol}_callbacks=", chain)
- target.__define_runner(symbol)
+ target.__reset_runner(symbol)
end
self.send("_#{symbol}_callbacks=", callbacks.dup.clear)
- __define_runner(symbol)
+ __reset_runner(symbol)
end
# Define sets of events in the object lifecycle that support callbacks.
@@ -560,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.
@@ -613,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/concern.rb b/activesupport/lib/active_support/concern.rb
index 81fb859334..c94a8d99f4 100644
--- a/activesupport/lib/active_support/concern.rb
+++ b/activesupport/lib/active_support/concern.rb
@@ -4,17 +4,12 @@ module ActiveSupport
# module M
# def self.included(base)
# base.extend ClassMethods
- # base.send(:include, InstanceMethods)
# scope :disabled, where(:disabled => true)
# end
#
# module ClassMethods
# ...
# end
- #
- # module InstanceMethods
- # ...
- # end
# end
#
# By using <tt>ActiveSupport::Concern</tt> the above module could instead be written as:
@@ -31,10 +26,6 @@ module ActiveSupport
# module ClassMethods
# ...
# end
- #
- # module InstanceMethods
- # ...
- # end
# end
#
# Moreover, it gracefully handles module dependencies. Given a +Foo+ module and a +Bar+
@@ -118,7 +109,6 @@ module ActiveSupport
@_dependencies.each { |dep| base.send(:include, dep) }
super
base.extend const_get("ClassMethods") if const_defined?("ClassMethods")
- base.send :include, const_get("InstanceMethods") if const_defined?("InstanceMethods")
base.class_eval(&@_included_block) if instance_variable_defined?("@_included_block")
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.rb b/activesupport/lib/active_support/core_ext.rb
index 46a8609dd7..b48bdf08e8 100644
--- a/activesupport/lib/active_support/core_ext.rb
+++ b/activesupport/lib/active_support/core_ext.rb
@@ -1,3 +1,4 @@
Dir["#{File.dirname(__FILE__)}/core_ext/*.rb"].sort.each do |path|
+ next if File.basename(path, '.rb') == 'logger'
require "active_support/core_ext/#{File.basename(path, '.rb')}"
end
diff --git a/activesupport/lib/active_support/core_ext/array.rb b/activesupport/lib/active_support/core_ext/array.rb
index 4688468a8f..79ba79192a 100644
--- a/activesupport/lib/active_support/core_ext/array.rb
+++ b/activesupport/lib/active_support/core_ext/array.rb
@@ -4,4 +4,4 @@ require 'active_support/core_ext/array/uniq_by'
require 'active_support/core_ext/array/conversions'
require 'active_support/core_ext/array/extract_options'
require 'active_support/core_ext/array/grouping'
-require 'active_support/core_ext/array/random_access'
+require 'active_support/core_ext/array/prepend_and_append'
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 3b22e8b4f9..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
@@ -39,20 +43,20 @@ class Array
#
# Blog.all.to_formatted_s # => "First PostSecond PostThird Post"
#
- # Adding in the <tt>:db</tt> argument as the format yields a prettier
- # output:
+ # Adding in the <tt>:db</tt> argument as the format yields a comma separated
+ # id list:
#
- # Blog.all.to_formatted_s(:db) # => "First Post,Second Post,Third Post"
+ # 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 4cd9bfadac..ac1ae53db0 100644
--- a/activesupport/lib/active_support/core_ext/array/grouping.rb
+++ b/activesupport/lib/active_support/core_ext/array/grouping.rb
@@ -1,5 +1,3 @@
-require 'enumerator'
-
class Array
# Splits or iterates over the array in groups of size +number+,
# padding any remaining slots with +fill_with+ unless it is +false+.
@@ -84,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/prepend_and_append.rb b/activesupport/lib/active_support/core_ext/array/prepend_and_append.rb
new file mode 100644
index 0000000000..27718f19d4
--- /dev/null
+++ b/activesupport/lib/active_support/core_ext/array/prepend_and_append.rb
@@ -0,0 +1,7 @@
+class Array
+ # The human way of thinking about adding stuff to the end of a list is with append
+ alias_method :append, :<<
+
+ # The human way of thinking about adding stuff to the beginning of a list is with prepend
+ alias_method :prepend, :unshift
+end \ No newline at end of file
diff --git a/activesupport/lib/active_support/core_ext/array/random_access.rb b/activesupport/lib/active_support/core_ext/array/random_access.rb
deleted file mode 100644
index bb1807a68a..0000000000
--- a/activesupport/lib/active_support/core_ext/array/random_access.rb
+++ /dev/null
@@ -1,30 +0,0 @@
-class Array
- # Backport of Array#sample based on Marc-Andre Lafortune's https://github.com/marcandre/backports/
- # Returns a random element or +n+ random elements from the array.
- # If the array is empty and +n+ is nil, returns <tt>nil</tt>.
- # If +n+ is passed and its value is less than 0, it raises an +ArgumentError+ exception.
- # If the value of +n+ is equal or greater than 0 it returns <tt>[]</tt>.
- #
- # [1,2,3,4,5,6].sample # => 4
- # [1,2,3,4,5,6].sample(3) # => [2, 4, 5]
- # [1,2,3,4,5,6].sample(-3) # => ArgumentError: negative array size
- # [].sample # => nil
- # [].sample(3) # => []
- def sample(n=nil)
- return self[Kernel.rand(size)] if n.nil?
- n = n.to_int
- rescue Exception => e
- raise TypeError, "Coercion error: #{n.inspect}.to_int => Integer failed:\n(#{e.message})"
- else
- raise TypeError, "Coercion error: obj.to_int did NOT return an Integer (was #{n.class})" unless n.kind_of? Integer
- raise ArgumentError, "negative array size" if n < 0
- n = size if n > size
- result = Array.new(self)
- n.times do |i|
- r = i + Kernel.rand(size - i)
- result[i], result[r] = result[r], result[i]
- end
- result[n..size] = []
- result
- end unless method_defined? :sample
-end
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 9c5f97b0e9..3bedfa9a61 100644
--- a/activesupport/lib/active_support/core_ext/array/uniq_by.rb
+++ b/activesupport/lib/active_support/core_ext/array/uniq_by.rb
@@ -1,16 +1,20 @@
class Array
- # Returns an unique array based on the criteria given as a +Proc+.
+ # *DEPRECATED*: Use +Array#uniq+ instead.
+ #
+ # Returns a unique array based on the criteria in the block.
#
# [1, 2, 3, 4].uniq_by { |i| i.odd? } # => [1, 2]
#
- def uniq_by
- hash, array = {}, []
- each { |i| hash[yield(i)] ||= (array << i) }
- array
+ def uniq_by(&block)
+ ActiveSupport::Deprecation.warn 'uniq_by is deprecated. Use Array#uniq instead', caller
+ uniq(&block)
end
- # Same as uniq_by, but modifies self.
- def uniq_by!
- replace(uniq_by{ |i| yield(i) })
+ # *DEPRECATED*: Use +Array#uniq!+ instead.
+ #
+ # Same as +uniq_by+, but modifies +self+.
+ def uniq_by!(&block)
+ 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/big_decimal/conversions.rb b/activesupport/lib/active_support/core_ext/big_decimal/conversions.rb
index 080604147d..3ec7e576c8 100644
--- a/activesupport/lib/active_support/core_ext/big_decimal/conversions.rb
+++ b/activesupport/lib/active_support/core_ext/big_decimal/conversions.rb
@@ -1,36 +1,19 @@
require 'bigdecimal'
-
-begin
- require 'psych'
-rescue LoadError
-end
-
require 'yaml'
class BigDecimal
- YAML_TAG = 'tag:yaml.org,2002:float'
YAML_MAPPING = { 'Infinity' => '.Inf', '-Infinity' => '-.Inf', 'NaN' => '.NaN' }
- # This emits the number without any scientific notation.
- # This is better than self.to_f.to_s since it doesn't lose precision.
- #
- # Note that reconstituting YAML floats to native floats may lose precision.
- def to_yaml(opts = {})
- return super if defined?(YAML::ENGINE) && !YAML::ENGINE.syck?
-
- YAML.quick_emit(nil, opts) do |out|
- string = to_s
- out.scalar(YAML_TAG, YAML_MAPPING[string] || string, :plain)
- end
- end
-
def encode_with(coder)
string = to_s
coder.represent_scalar(nil, YAML_MAPPING[string] || string)
end
- def to_d
- self
+ # Backport this method if it doesn't exist
+ unless method_defined?(:to_d)
+ def to_d
+ self
+ end
end
DEFAULT_STRING_FORMAT = 'F'
diff --git a/activesupport/lib/active_support/core_ext/class/attribute.rb b/activesupport/lib/active_support/core_ext/class/attribute.rb
index 45bec264ff..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?
- !name || '' == name
- 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/class/subclasses.rb b/activesupport/lib/active_support/core_ext/class/subclasses.rb
index 46e9daaa8f..74ea047c24 100644
--- a/activesupport/lib/active_support/core_ext/class/subclasses.rb
+++ b/activesupport/lib/active_support/core_ext/class/subclasses.rb
@@ -7,7 +7,7 @@ class Class #:nodoc:
def descendants
descendants = []
- ObjectSpace.each_object(class << self; self; end) do |k|
+ ObjectSpace.each_object(singleton_class) do |k|
descendants.unshift k unless k == self
end
descendants
diff --git a/activesupport/lib/active_support/core_ext/date/calculations.rb b/activesupport/lib/active_support/core_ext/date/calculations.rb
index 26a99658cc..323126fcfa 100644
--- a/activesupport/lib/active_support/core_ext/date/calculations.rb
+++ b/activesupport/lib/active_support/core_ext/date/calculations.rb
@@ -5,25 +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 }
-
- if RUBY_VERSION < '1.9'
- undef :>>
-
- # Backported from 1.9. The one in 1.8 leads to incorrect next_month and
- # friends for dates where the calendar reform is involved. It additionally
- # prevents an infinite loop fixed in r27013.
- def >>(n)
- y, m = (year * 12 + (mon - 1) + n).divmod(12)
- m, = (m + 1) .divmod(1)
- d = mday
- until jd2 = self.class.valid_civil?(y, m, d, start)
- d -= 1
- raise ArgumentError, 'invalid date' unless d > 0
- end
- self + (jd2 - jd)
- end
- end
+ 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).
@@ -49,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.
@@ -123,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
@@ -154,90 +144,114 @@ class Date
advance(:years => years)
end
- # Shorthand for years_ago(1)
- def prev_year
- years_ago(1)
- end unless method_defined?(:prev_year)
-
- # Shorthand for years_since(1)
- def next_year
- years_since(1)
- end unless method_defined?(:next_year)
-
- # Shorthand for months_ago(1)
- def prev_month
- months_ago(1)
- end unless method_defined?(:prev_month)
-
- # Shorthand for months_since(1)
- def next_month
- months_since(1)
- end unless method_defined?(:next_month)
+ # Returns number of days to start of this week. Week is assumed to start on
+ # +start_day+, default is +:monday+.
+ def days_to_week_start(start_day = :monday)
+ start_day_number = DAYS_INTO_WEEK[start_day]
+ current_day_number = wday != 0 ? wday - 1 : 6
+ (current_day_number - start_day_number) % 7
+ end
- # Returns a new Date/DateTime representing the "start" of this week (i.e, Monday; DateTime objects will have time set to 0:00).
- def beginning_of_week
- days_to_monday = self.wday!=0 ? self.wday-1 : 6
- result = self - days_to_monday
- self.acts_like?(:time) ? result.midnight : result
+ # Returns a new +Date+/+DateTime+ representing the start of this week. Week is
+ # assumed to start on +start_day+, default is +:monday+. +DateTime+ objects
+ # have their time set to 0:00.
+ def beginning_of_week(start_day = :monday)
+ days_to_start = days_to_week_start(start_day)
+ result = self - days_to_start
+ acts_like?(:time) ? result.midnight : result
end
- alias :monday :beginning_of_week
alias :at_beginning_of_week :beginning_of_week
- # Returns a new Date/DateTime representing the end of this week (Sunday, DateTime objects will have time set to 23:59:59).
- def end_of_week
- days_to_sunday = self.wday!=0 ? 7-self.wday : 0
- result = self + days_to_sunday.days
- self.acts_like?(:time) ? result.end_of_day : result
+ # Returns a new +Date+/+DateTime+ representing the start of this week. Week is
+ # assumed to start on a Monday. +DateTime+ objects have their time set to 0:00.
+ def monday
+ beginning_of_week
+ end
+
+ # Returns a new +Date+/+DateTime+ representing the end of this week. Week is
+ # assumed to start on +start_day+, default is +:monday+. +DateTime+ objects
+ # have their time set to 23:59:59.
+ def end_of_week(start_day = :monday)
+ days_to_end = 6 - days_to_week_start(start_day)
+ result = self + days_to_end.days
+ acts_like?(:time) ? result.end_of_day : result
end
- alias :sunday :end_of_week
alias :at_end_of_week :end_of_week
- # Returns a new Date/DateTime representing the start of the given day in the previous week (default is Monday).
+ # Returns a new +Date+/+DateTime+ representing the end of this week. Week is
+ # assumed to start on a Monday. +DateTime+ objects have their time set to 23:59:59.
+ def sunday
+ end_of_week
+ end
+
+ # Returns a new +Date+/+DateTime+ representing the given +day+ in the previous
+ # 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).
+ # 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 338104fd05..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,17 +61,11 @@ 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
- # A method to keep Time, Date and DateTime instances interchangeable on conversions.
- # In this case, it simply returns +self+.
- def to_date
- self
- end if RUBY_VERSION < '1.9'
-
# Converts a Date instance to a Time, where the time is set to the beginning of the day.
# The timezone can be either :local or :utc (default :local).
#
@@ -83,23 +80,6 @@ class Date
::Time.send("#{form}_time", year, month, day)
end
- # Converts a Date instance to a DateTime, where the time is set to the beginning of the day
- # and UTC offset is set to 0.
- #
- # ==== Examples
- # date = Date.new(2007, 11, 10) # => Sat, 10 Nov 2007
- #
- # date.to_datetime # => Sat, 10 Nov 2007 00:00:00 0000
- def to_datetime
- ::DateTime.civil(year, month, day, 0, 0, 0, 0)
- end if RUBY_VERSION < '1.9'
-
- def iso8601
- strftime('%F')
- end if RUBY_VERSION < '1.9'
-
- alias_method :rfc3339, :iso8601 if RUBY_VERSION < '1.9'
-
def xmlschema
to_time_in_current_zone.xmlschema
end
diff --git a/activesupport/lib/active_support/core_ext/date/freeze.rb b/activesupport/lib/active_support/core_ext/date/freeze.rb
deleted file mode 100644
index a731f8345e..0000000000
--- a/activesupport/lib/active_support/core_ext/date/freeze.rb
+++ /dev/null
@@ -1,33 +0,0 @@
-# Date memoizes some instance methods using metaprogramming to wrap
-# the methods with one that caches the result in an instance variable.
-#
-# If a Date is frozen but the memoized method hasn't been called, the
-# first call will result in a frozen object error since the memo
-# instance variable is uninitialized.
-#
-# Work around by eagerly memoizing before the first freeze.
-#
-# Ruby 1.9 uses a preinitialized instance variable so it's unaffected.
-# This hack is as close as we can get to feature detection:
-if RUBY_VERSION < '1.9'
- require 'date'
- begin
- ::Date.today.freeze.jd
- rescue => frozen_object_error
- if frozen_object_error.message =~ /frozen/
- class Date #:nodoc:
- def freeze
- unless frozen?
- self.class.private_instance_methods(false).each do |m|
- if m.to_s =~ /\A__\d+__\Z/
- instance_variable_set(:"@#{m}", [send(m)])
- end
- end
- end
-
- super
- end
- end
- end
- end
-end
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 48cf1a435d..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,10 +1,12 @@
-require 'rational' unless RUBY_VERSION >= '1.9.2'
+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>.
@@ -33,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
@@ -51,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
@@ -81,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:
@@ -129,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 ca899c714c..6338dc6397 100644
--- a/activesupport/lib/active_support/core_ext/date_time/conversions.rb
+++ b/activesupport/lib/active_support/core_ext/date_time/conversions.rb
@@ -6,7 +6,7 @@ require 'active_support/values/time_zone'
class DateTime
# Ruby 1.9 has DateTime#to_time which internally relies on Time. We define our own #to_time which allows
# DateTimes outside the range of what can be created with Time.
- remove_method :to_time if instance_methods.include?(:to_time)
+ remove_method :to_time
# Convert to a formatted string. See Time::DATE_FORMATS for predefined formats.
#
@@ -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 * (RUBY_VERSION < '1.9' ? 86400000000 : 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 ddb4f3012f..b9c632e4f5 100644
--- a/activesupport/lib/active_support/core_ext/enumerable.rb
+++ b/activesupport/lib/active_support/core_ext/enumerable.rb
@@ -1,41 +1,4 @@
-require 'active_support/ordered_hash'
-
module Enumerable
- # Ruby 1.8.7 introduces group_by, but the result isn't ordered. Override it.
- remove_method(:group_by) if [].respond_to?(:group_by) && RUBY_VERSION < '1.9'
-
- # Collect an enumerable into sets, grouped by the result of a block. Useful,
- # for example, for grouping records by date.
- #
- # Example:
- #
- # latest_transcripts.group_by(&:day).each do |day, transcripts|
- # p "#{day} -> #{transcripts.map(&:class).join(', ')}"
- # end
- # "2006-03-01 -> Transcript"
- # "2006-02-28 -> Transcript"
- # "2006-02-27 -> Transcript, Transcript"
- # "2006-02-26 -> Transcript, Transcript"
- # "2006-02-25 -> Transcript"
- # "2006-02-24 -> Transcript, Transcript"
- # "2006-02-23 -> Transcript"
- def group_by
- return to_enum :group_by unless block_given?
- assoc = ActiveSupport::OrderedHash.new
-
- each do |element|
- key = yield(element)
-
- if assoc.has_key?(key)
- assoc[key] << element
- else
- assoc[key] = [element]
- end
- end
-
- assoc
- end unless [].respond_to?(:group_by)
-
# Calculates a sum from the elements. Examples:
#
# payments.sum { |p| p.price * p.tax_rate }
@@ -48,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:
@@ -63,27 +26,6 @@ module Enumerable
end
end
- # Iterates over a collection, passing the current element *and* the
- # +memo+ to the block. Handy for building up hashes or
- # reducing collections down to one object. Examples:
- #
- # %w(foo bar).each_with_object({}) { |str, hsh| hsh[str] = str.upcase }
- # # => {'foo' => 'FOO', 'bar' => 'BAR'}
- #
- # *Note* that you can't use immutable objects like numbers, true or false as
- # the memo. You would think the following returns 120, but since the memo is
- # never changed, it does not.
- #
- # (1..5).each_with_object(1) { |value, memo| memo *= value } # => 1
- #
- def each_with_object(memo)
- return to_enum :each_with_object, memo unless block_given?
- each do |element|
- yield element, memo
- end
- memo
- end unless [].respond_to?(:each_with_object)
-
# Convert an enumerable to a hash. Examples:
#
# people.index_by(&:login)
@@ -92,12 +34,15 @@ 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.
- # Can be called with a block too, much like any?, so people.many? { |p| p.age > 26 } returns true if more than 1 person is over 26.
+ # Can be called with a block too, much like any?, so <tt>people.many? { |p| p.age > 26 }</tt> returns true if more than one person is over 26.
def many?
cnt = 0
if block_given?
@@ -106,11 +51,11 @@ module Enumerable
cnt > 1
end
else
- any?{ (cnt += 1) > 1 }
+ any? { (cnt += 1) > 1 }
end
end
- # The negative of the Enumerable#include?. Returns true if the collection does not include the object.
+ # The negative of the <tt>Enumerable#include?</tt>. Returns true if the collection does not include the object.
def exclude?(object)
!include?(object)
end
@@ -120,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/exception.rb b/activesupport/lib/active_support/core_ext/exception.rb
index ef801e713d..ba7757ea07 100644
--- a/activesupport/lib/active_support/core_ext/exception.rb
+++ b/activesupport/lib/active_support/core_ext/exception.rb
@@ -1,3 +1,3 @@
module ActiveSupport
- FrozenObjectError = RUBY_VERSION < '1.9' ? TypeError : RuntimeError
+ FrozenObjectError = RuntimeError
end
diff --git a/activesupport/lib/active_support/core_ext/file.rb b/activesupport/lib/active_support/core_ext/file.rb
index a763447566..dc24afbe7f 100644
--- a/activesupport/lib/active_support/core_ext/file.rb
+++ b/activesupport/lib/active_support/core_ext/file.rb
@@ -1,2 +1 @@
require 'active_support/core_ext/file/atomic'
-require 'active_support/core_ext/file/path'
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/file/path.rb b/activesupport/lib/active_support/core_ext/file/path.rb
deleted file mode 100644
index b5feab80ae..0000000000
--- a/activesupport/lib/active_support/core_ext/file/path.rb
+++ /dev/null
@@ -1,5 +0,0 @@
-class File
- unless File.allocate.respond_to?(:to_path)
- alias to_path path
- end
-end \ No newline at end of file
diff --git a/activesupport/lib/active_support/core_ext/float.rb b/activesupport/lib/active_support/core_ext/float.rb
deleted file mode 100644
index 7570471b95..0000000000
--- a/activesupport/lib/active_support/core_ext/float.rb
+++ /dev/null
@@ -1 +0,0 @@
-require 'active_support/core_ext/float/rounding'
diff --git a/activesupport/lib/active_support/core_ext/float/rounding.rb b/activesupport/lib/active_support/core_ext/float/rounding.rb
deleted file mode 100644
index 0d4fb87665..0000000000
--- a/activesupport/lib/active_support/core_ext/float/rounding.rb
+++ /dev/null
@@ -1,19 +0,0 @@
-class Float
- alias precisionless_round round
- private :precisionless_round
-
- # Rounds the float with the specified precision.
- #
- # x = 1.337
- # x.round # => 1
- # x.round(1) # => 1.3
- # x.round(2) # => 1.34
- def round(precision = nil)
- if precision
- magnitude = 10.0 ** precision
- (self * magnitude).round / magnitude
- else
- precisionless_round
- end
- end
-end if RUBY_VERSION < '1.9'
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 0b368fe7b7..7d54c9fae6 100644
--- a/activesupport/lib/active_support/core_ext/hash/indifferent_access.rb
+++ b/activesupport/lib/active_support/core_ext/hash/indifferent_access.rb
@@ -2,7 +2,7 @@ require 'active_support/hash_with_indifferent_access'
class Hash
- # Returns an +ActiveSupport::HashWithIndifferentAccess+ out of its receiver:
+ # Returns an <tt>ActiveSupport::HashWithIndifferentAccess</tt> out of its receiver:
#
# {:a => 1}.with_indifferent_access["a"] # => 1
#
@@ -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 d7fb2da0fb..fdeea4c148 100644
--- a/activesupport/lib/active_support/core_ext/hash/slice.rb
+++ b/activesupport/lib/active_support/core_ext/hash/slice.rb
@@ -13,26 +13,24 @@ 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
# {: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)
omit
end
+ # 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/io.rb b/activesupport/lib/active_support/core_ext/io.rb
deleted file mode 100644
index 75f1055191..0000000000
--- a/activesupport/lib/active_support/core_ext/io.rb
+++ /dev/null
@@ -1,15 +0,0 @@
-if RUBY_VERSION < '1.9.2'
-
-# :stopdoc:
-class IO
- def self.binread(name, length = nil, offset = nil)
- return File.read name unless length || offset
- File.open(name, 'rb') { |f|
- f.seek offset if offset
- f.read length
- }
- end
-end
-# :startdoc:
-
-end
diff --git a/activesupport/lib/active_support/core_ext/kernel/debugger.rb b/activesupport/lib/active_support/core_ext/kernel/debugger.rb
index 7516f41e0b..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-debug 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/kernel/singleton_class.rb b/activesupport/lib/active_support/core_ext/kernel/singleton_class.rb
index 33612155fb..9bbf1bbd73 100644
--- a/activesupport/lib/active_support/core_ext/kernel/singleton_class.rb
+++ b/activesupport/lib/active_support/core_ext/kernel/singleton_class.rb
@@ -1,11 +1,4 @@
module Kernel
- # Returns the object's singleton class.
- def singleton_class
- class << self
- self
- end
- end unless respond_to?(:singleton_class) # exists in 1.9.2
-
# class_eval on an object acts like singleton_class.class_eval.
def class_eval(*args, &block)
singleton_class.class_eval(*args, &block)
diff --git a/activesupport/lib/active_support/core_ext/logger.rb b/activesupport/lib/active_support/core_ext/logger.rb
index e63a0a9ed9..16fce81445 100644
--- a/activesupport/lib/active_support/core_ext/logger.rb
+++ b/activesupport/lib/active_support/core_ext/logger.rb
@@ -1,4 +1,7 @@
require 'active_support/core_ext/class/attribute_accessors'
+require 'active_support/deprecation'
+
+ActiveSupport::Deprecation.warn 'this file is deprecated and will be removed'
# Adds the 'around_level' method to Logger.
class Logger #:nodoc:
@@ -53,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.rb b/activesupport/lib/active_support/core_ext/module.rb
index 9fed346b7c..f2d4887df6 100644
--- a/activesupport/lib/active_support/core_ext/module.rb
+++ b/activesupport/lib/active_support/core_ext/module.rb
@@ -5,7 +5,6 @@ require 'active_support/core_ext/module/reachable'
require 'active_support/core_ext/module/attribute_accessors'
require 'active_support/core_ext/module/attr_internal'
require 'active_support/core_ext/module/delegation'
-require 'active_support/core_ext/module/synchronization'
require 'active_support/core_ext/module/deprecation'
require 'active_support/core_ext/module/remove_method'
-require 'active_support/core_ext/module/method_names' \ No newline at end of file
+require 'active_support/core_ext/module/qualified_const'
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/anonymous.rb b/activesupport/lib/active_support/core_ext/module/anonymous.rb
index 3982c9c586..0a9e791030 100644
--- a/activesupport/lib/active_support/core_ext/module/anonymous.rb
+++ b/activesupport/lib/active_support/core_ext/module/anonymous.rb
@@ -1,5 +1,3 @@
-require 'active_support/core_ext/object/blank'
-
class Module
# A module may or may not have a name.
#
@@ -7,7 +5,7 @@ class Module
# M.name # => "M"
#
# m = Module.new
- # m.name # => ""
+ # m.name # => nil
#
# A module gets a name when it is first assigned to a constant. Either
# via the +module+ or +class+ keyword or by an explicit assignment:
@@ -17,8 +15,6 @@ class Module
# m.name # => "M"
#
def anonymous?
- # Uses blank? because the name of an anonymous class is an empty
- # string in 1.8, and nil in 1.9.
- name.blank?
+ name.nil?
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 bf7e009290..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
@@ -123,24 +125,35 @@ class Module
line = line.to_i
methods.each do |method|
- on_nil =
- if allow_nil
- 'return'
- else
- %(raise "#{self}##{method_prefix}#{method} delegated to #{to}.#{method}, but #{to} is nil: \#{self.inspect}")
- end
+ method = method.to_s
+
+ # Attribute writer methods only accept one argument. Makes sure []=
+ # methods still accept two arguments.
+ definition = (method =~ /[^\]]=$/) ? 'arg' : '*args, &block'
- module_eval(<<-EOS, file, line - 5)
- def #{method_prefix}#{method}(*args, &block) # def customer_name(*args, &block)
- #{to}.__send__(#{method.inspect}, *args, &block) # client.__send__(:name, *args, &block)
- rescue NoMethodError # rescue NoMethodError
- if #{to}.nil? # if client.nil?
- #{on_nil} # return # depends on :allow_nil
- else # else
- raise # raise
- end # end
- end # end
- EOS
+ if allow_nil
+ module_eval(<<-EOS, file, line - 2)
+ def #{method_prefix}#{method}(#{definition}) # def customer_name(*args, &block)
+ if #{to} || #{to}.respond_to?(:#{method}) # if client || client.respond_to?(:name)
+ #{to}.#{method}(#{definition}) # client.name(*args, &block)
+ end # end
+ end # end
+ EOS
+ else
+ 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}(#{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
+ else # else
+ raise # raise
+ end # end
+ end # end
+ EOS
+ end
end
end
end
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 c08ad251dd..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.
@@ -57,32 +58,23 @@ class Module
parents
end
- if RUBY_VERSION < '1.9'
- # Returns the constants that have been defined locally by this object and
- # not in an ancestor. This method is exact if running under Ruby 1.9. In
- # previous versions it may miss some constants if their definition in some
- # ancestor is identical to their definition in the receiver.
- def local_constants
- inherited = {}
-
- ancestors.each do |anc|
- next if anc == self
- anc.constants.each { |const| inherited[const] = anc.const_get(const) }
- end
-
- constants.select do |const|
- !inherited.key?(const) || inherited[const].object_id != const_get(const).object_id
- end
- end
- else
- def local_constants #:nodoc:
- constants(false)
- end
+ def local_constants #:nodoc:
+ 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/method_names.rb b/activesupport/lib/active_support/core_ext/module/method_names.rb
deleted file mode 100644
index 2eb40a83ab..0000000000
--- a/activesupport/lib/active_support/core_ext/module/method_names.rb
+++ /dev/null
@@ -1,14 +0,0 @@
-class Module
- if instance_methods[0].is_a?(Symbol)
- def instance_method_names(*args)
- instance_methods(*args).map(&:to_s)
- end
-
- def method_names(*args)
- methods(*args).map(&:to_s)
- end
- else
- alias_method :instance_method_names, :instance_methods
- alias_method :method_names, :methods
- end
-end \ No newline at end of file
diff --git a/activesupport/lib/active_support/core_ext/module/qualified_const.rb b/activesupport/lib/active_support/core_ext/module/qualified_const.rb
new file mode 100644
index 0000000000..65525013db
--- /dev/null
+++ b/activesupport/lib/active_support/core_ext/module/qualified_const.rb
@@ -0,0 +1,52 @@
+require 'active_support/core_ext/string/inflections'
+
+#--
+# Allows code reuse in the methods below without polluting Module.
+#++
+module QualifiedConstUtils
+ def self.raise_if_absolute(path)
+ raise NameError.new("wrong constant name #$&") if path =~ /\A::[^:]+/
+ end
+
+ def self.names(path)
+ path.split('::')
+ end
+end
+
+##
+# Extends the API for constants to be able to deal with qualified names. Arguments
+# are assumed to be relative to the receiver.
+#
+#--
+# 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.
+#++
+class Module
+ def qualified_const_defined?(path, search_parents=true)
+ QualifiedConstUtils.raise_if_absolute(path)
+
+ QualifiedConstUtils.names(path).inject(self) do |mod, name|
+ return unless mod.const_defined?(name, search_parents)
+ mod.const_get(name)
+ end
+ return true
+ end
+
+ def qualified_const_get(path)
+ QualifiedConstUtils.raise_if_absolute(path)
+
+ QualifiedConstUtils.names(path).inject(self) do |mod, name|
+ mod.const_get(name)
+ end
+ end
+
+ def qualified_const_set(path, value)
+ QualifiedConstUtils.raise_if_absolute(path)
+
+ const_name = path.demodulize
+ mod_name = path.deconstantize
+ mod = mod_name.empty? ? self : qualified_const_get(mod_name)
+ mod.const_set(const_name, value)
+ end
+end
diff --git a/activesupport/lib/active_support/core_ext/module/reachable.rb b/activesupport/lib/active_support/core_ext/module/reachable.rb
index 443d2c3d53..5d3d0e9851 100644
--- a/activesupport/lib/active_support/core_ext/module/reachable.rb
+++ b/activesupport/lib/active_support/core_ext/module/reachable.rb
@@ -3,8 +3,6 @@ require 'active_support/core_ext/string/inflections'
class Module
def reachable? #:nodoc:
- !anonymous? && name.constantize.equal?(self)
- rescue NameError
- false
+ !anonymous? && name.safe_constantize.equal?(self)
end
end
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/module/synchronization.rb b/activesupport/lib/active_support/core_ext/module/synchronization.rb
deleted file mode 100644
index ed16c2f71b..0000000000
--- a/activesupport/lib/active_support/core_ext/module/synchronization.rb
+++ /dev/null
@@ -1,43 +0,0 @@
-require 'thread'
-require 'active_support/core_ext/module/aliasing'
-require 'active_support/core_ext/array/extract_options'
-
-class Module
- # Synchronize access around a method, delegating synchronization to a
- # particular mutex. A mutex (either a Mutex, or any object that responds to
- # #synchronize and yields to a block) must be provided as a final :with option.
- # The :with option should be a symbol or string, and can represent a method,
- # constant, or instance or class variable.
- # Example:
- # class SharedCache
- # @@lock = Mutex.new
- # def expire
- # ...
- # end
- # synchronize :expire, :with => :@@lock
- # end
- def synchronize(*methods)
- options = methods.extract_options!
- unless options.is_a?(Hash) && with = options[:with]
- raise ArgumentError, "Synchronization needs a mutex. Supply an options hash with a :with key as the last argument (e.g. synchronize :hello, :with => :@mutex)."
- end
-
- methods.each do |method|
- aliased_method, punctuation = method.to_s.sub(/([?!=])$/, ''), $1
-
- if method_defined?("#{aliased_method}_without_synchronization#{punctuation}")
- raise ArgumentError, "#{method} is already synchronized. Double synchronization is not currently supported."
- end
-
- module_eval(<<-EOS, __FILE__, __LINE__ + 1)
- def #{aliased_method}_with_synchronization#{punctuation}(*args, &block) # def expire_with_synchronization(*args, &block)
- #{with}.synchronize do # @@lock.synchronize do
- #{aliased_method}_without_synchronization#{punctuation}(*args, &block) # expire_without_synchronization(*args, &block)
- end # end
- end # end
- EOS
-
- alias_method_chain method, :synchronization
- end
- end
-end
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 fe27f45295..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:
#
@@ -89,23 +88,15 @@ class Hash
end
class String
- # 0x3000: fullwidth whitespace
- NON_WHITESPACE_REGEXP = %r![^\s#{[0x3000].pack("U")}]!
-
# 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?
- # 1.8 does not takes [:space:] properly
- if encoding_aware?
- self !~ /[^[:space:]]/
- else
- self !~ NON_WHITESPACE_REGEXP
- end
+ self !~ /[^[:space:]]/
end
end
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 b5671f66d0..3fec465ec0 100644
--- a/activesupport/lib/active_support/core_ext/object/inclusion.rb
+++ b/activesupport/lib/active_support/core_ext/object/inclusion.rb
@@ -1,15 +1,25 @@
class Object
- # Returns true if this object is included in the argument. Argument must be
- # any object which responds to +#include?+. Usage:
+ # 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
+ # characters = ['Konata', 'Kagami', 'Tsukasa']
+ # 'Konata'.in?(characters) # => true
#
- # This will throw an ArgumentError if the argument doesn't respond
+ # 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?+.
- def in?(another_object)
- another_object.include?(self)
- rescue NoMethodError
- raise ArgumentError.new("The parameter passed to #in? must respond to #include?")
+ def in?(*args)
+ if args.length > 1
+ args.include? self
+ else
+ another_object = args.first
+ if another_object.respond_to? :include?
+ another_object.include? self
+ else
+ raise ArgumentError.new 'The single parameter passed to #in? must respond to #include?'
+ end
+ 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 eda9694614..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
@@ -23,11 +23,7 @@ class Object
# end
#
# C.new(0, 1).instance_variable_names # => ["@y", "@x"]
- if RUBY_VERSION >= '1.9'
- def instance_variable_names
- instance_variables.map { |var| var.to_s }
- end
- else
- alias_method :instance_variable_names, :instance_variables
+ def instance_variable_names
+ instance_variables.map { |var| var.to_s }
end
end
diff --git a/activesupport/lib/active_support/core_ext/object/to_json.rb b/activesupport/lib/active_support/core_ext/object/to_json.rb
index 14ef27340e..e7dc60a612 100644
--- a/activesupport/lib/active_support/core_ext/object/to_json.rb
+++ b/activesupport/lib/active_support/core_ext/object/to_json.rb
@@ -10,10 +10,10 @@ end
# several cases (for instance, the JSON implementation for Hash does not work) with inheritance
# and consequently classes as ActiveSupport::OrderedHash cannot be serialized to json.
[Object, Array, FalseClass, Float, Hash, Integer, NilClass, String, TrueClass].each do |klass|
- klass.class_eval <<-RUBY, __FILE__, __LINE__
+ klass.class_eval do
# Dumps object in JSON (JavaScript Object Notation). See www.json.org for more info.
def to_json(options = nil)
ActiveSupport::JSON.encode(self, options)
end
- RUBY
+ end
end
diff --git a/activesupport/lib/active_support/core_ext/object/to_query.rb b/activesupport/lib/active_support/core_ext/object/to_query.rb
index 3f1540f685..5d5fcf00e0 100644
--- a/activesupport/lib/active_support/core_ext/object/to_query.rb
+++ b/activesupport/lib/active_support/core_ext/object/to_query.rb
@@ -7,7 +7,7 @@ class Object
# Note: This method is defined as a default implementation for all Objects for Hash#to_query to work.
def to_query(key)
require 'cgi' unless defined?(CGI) && defined?(CGI::escape)
- "#{CGI.escape(key.to_s)}=#{CGI.escape(to_param.to_s)}"
+ "#{CGI.escape(key.to_param)}=#{CGI.escape(to_param.to_s)}"
end
end
diff --git a/activesupport/lib/active_support/core_ext/object/try.rb b/activesupport/lib/active_support/core_ext/object/try.rb
index 4797c93e63..e77a9da0ec 100644
--- a/activesupport/lib/active_support/core_ext/object/try.rb
+++ b/activesupport/lib/active_support/core_ext/object/try.rb
@@ -28,8 +28,6 @@ class Object
def try(*a, &b)
if a.empty? && block_given?
yield self
- elsif !a.empty? && !respond_to?(a.first)
- nil
else
__send__(*a, &b)
end
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/process.rb b/activesupport/lib/active_support/core_ext/process.rb
deleted file mode 100644
index 0b0bc6dc69..0000000000
--- a/activesupport/lib/active_support/core_ext/process.rb
+++ /dev/null
@@ -1 +0,0 @@
-require 'active_support/core_ext/process/daemon'
diff --git a/activesupport/lib/active_support/core_ext/process/daemon.rb b/activesupport/lib/active_support/core_ext/process/daemon.rb
deleted file mode 100644
index f5202ddee4..0000000000
--- a/activesupport/lib/active_support/core_ext/process/daemon.rb
+++ /dev/null
@@ -1,23 +0,0 @@
-module Process
- def self.daemon(nochdir = nil, noclose = nil)
- exit if fork # Parent exits, child continues.
- Process.setsid # Become session leader.
- exit if fork # Zap session leader. See [1].
-
- unless nochdir
- Dir.chdir "/" # Release old working directory.
- end
-
- File.umask 0000 # Ensure sensible umask. Adjust as needed.
-
- unless noclose
- STDIN.reopen "/dev/null" # Free file descriptors and
- STDOUT.reopen "/dev/null", "a" # point them somewhere sensible.
- STDERR.reopen '/dev/null', 'a'
- end
-
- trap("TERM") { exit }
-
- return 0
- end unless respond_to?(:daemon)
-end
diff --git a/activesupport/lib/active_support/core_ext/range.rb b/activesupport/lib/active_support/core_ext/range.rb
index 2428a02242..c0736f3a44 100644
--- a/activesupport/lib/active_support/core_ext/range.rb
+++ b/activesupport/lib/active_support/core_ext/range.rb
@@ -2,4 +2,3 @@ require 'active_support/core_ext/range/blockless_step'
require 'active_support/core_ext/range/conversions'
require 'active_support/core_ext/range/include_range'
require 'active_support/core_ext/range/overlaps'
-require 'active_support/core_ext/range/cover'
diff --git a/activesupport/lib/active_support/core_ext/range/blockless_step.rb b/activesupport/lib/active_support/core_ext/range/blockless_step.rb
index db42ef5c47..f687287f0d 100644
--- a/activesupport/lib/active_support/core_ext/range/blockless_step.rb
+++ b/activesupport/lib/active_support/core_ext/range/blockless_step.rb
@@ -1,27 +1,11 @@
require 'active_support/core_ext/module/aliasing'
class Range
- begin
- (1..2).step
- # Range#step doesn't return an Enumerator
- rescue LocalJumpError
- # Return an array when step is called without a block.
- def step_with_blockless(*args, &block)
- if block_given?
- step_without_blockless(*args, &block)
- else
- array = []
- step_without_blockless(*args) { |step| array << step }
- array
- end
- end
- else
- def step_with_blockless(*args, &block) #:nodoc:
- if block_given?
- step_without_blockless(*args, &block)
- else
- step_without_blockless(*args).to_a
- end
+ def step_with_blockless(*args, &block) #:nodoc:
+ if block_given?
+ step_without_blockless(*args, &block)
+ else
+ step_without_blockless(*args).to_a
end
end
diff --git a/activesupport/lib/active_support/core_ext/range/conversions.rb b/activesupport/lib/active_support/core_ext/range/conversions.rb
index 544e63132d..43134b4314 100644
--- a/activesupport/lib/active_support/core_ext/range/conversions.rb
+++ b/activesupport/lib/active_support/core_ext/range/conversions.rb
@@ -7,7 +7,7 @@ class Range
#
# ==== Example
#
- # [1..100].to_formatted_s # => "1..100"
+ # (1..100).to_formatted_s # => "1..100"
def to_formatted_s(format = :default)
if formatter = RANGE_FORMATS[format]
formatter.call(first, last)
diff --git a/activesupport/lib/active_support/core_ext/range/cover.rb b/activesupport/lib/active_support/core_ext/range/cover.rb
deleted file mode 100644
index 3a182cddd2..0000000000
--- a/activesupport/lib/active_support/core_ext/range/cover.rb
+++ /dev/null
@@ -1,3 +0,0 @@
-class Range
- alias_method(:cover?, :include?) unless instance_methods.include?(:cover?)
-end
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 0246627467..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,13 +5,13 @@ 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)
- operator = exclude_end? ? :< : :<=
- end_value = value.exclude_end? ? last.succ : last
- include_without_range?(value.first) && (value.last <=> end_value).send(operator, 0)
+ # 1...10 includes 1..9 but it does not include 1..10.
+ operator = exclude_end? && !value.exclude_end? ? :< : :<=
+ include_without_range?(value.first) && value.last.send(operator, last)
else
include_without_range?(value)
end
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/rexml.rb b/activesupport/lib/active_support/core_ext/rexml.rb
deleted file mode 100644
index 0419ebc84b..0000000000
--- a/activesupport/lib/active_support/core_ext/rexml.rb
+++ /dev/null
@@ -1,46 +0,0 @@
-require 'active_support/core_ext/kernel/reporting'
-
-# Fixes the rexml vulnerability disclosed at:
-# http://www.ruby-lang.org/en/news/2008/08/23/dos-vulnerability-in-rexml/
-# This fix is identical to rexml-expansion-fix version 1.0.1.
-#
-# We still need to distribute this fix because albeit the REXML
-# in recent 1.8.7s is patched, it wasn't in early patchlevels.
-require 'rexml/rexml'
-
-# Earlier versions of rexml defined REXML::Version, newer ones REXML::VERSION
-unless (defined?(REXML::VERSION) ? REXML::VERSION : REXML::Version) > "3.1.7.2"
- silence_warnings { require 'rexml/document' }
-
- # REXML in 1.8.7 has the patch but early patchlevels didn't update Version from 3.1.7.2.
- unless REXML::Document.respond_to?(:entity_expansion_limit=)
- silence_warnings { require 'rexml/entity' }
-
- module REXML #:nodoc:
- class Entity < Child #:nodoc:
- undef_method :unnormalized
- def unnormalized
- document.record_entity_expansion! if document
- v = value()
- return nil if v.nil?
- @unnormalized = Text::unnormalize(v, parent)
- @unnormalized
- end
- end
- class Document < Element #:nodoc:
- @@entity_expansion_limit = 10_000
- def self.entity_expansion_limit= val
- @@entity_expansion_limit = val
- end
-
- def record_entity_expansion!
- @number_of_expansions ||= 0
- @number_of_expansions += 1
- if @number_of_expansions > @@entity_expansion_limit
- raise "Number of entity expansions exceeded, processing aborted."
- end
- end
- end
- end
- 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 c0d5cdf2d5..23aaee9c43 100644
--- a/activesupport/lib/active_support/core_ext/string/access.rb
+++ b/activesupport/lib/active_support/core_ext/string/access.rb
@@ -1,99 +1,35 @@
-require "active_support/multibyte"
+require 'active_support/multibyte'
class String
- unless '1.9'.respond_to?(:force_encoding)
- # Returns the character at the +position+ treating the string as an array (where 0 is the first character).
- #
- # Examples:
- # "hello".at(0) # => "h"
- # "hello".at(4) # => "o"
- # "hello".at(10) # => ERROR if < 1.9, nil in 1.9
- def at(position)
- mb_chars[position, 1].to_s
- end
-
- # Returns the remaining of the string from the +position+ treating the string as an array (where 0 is the first character).
- #
- # Examples:
- # "hello".from(0) # => "hello"
- # "hello".from(2) # => "llo"
- # "hello".from(10) # => "" if < 1.9, nil in 1.9
- def from(position)
- mb_chars[position..-1].to_s
- end
-
- # Returns the beginning of the string up to the +position+ treating the string as an array (where 0 is the first character).
- #
- # Examples:
- # "hello".to(0) # => "h"
- # "hello".to(2) # => "hel"
- # "hello".to(10) # => "hello"
- def to(position)
- mb_chars[0..position].to_s
- end
-
- # Returns the first character of the string or the first +limit+ characters.
- #
- # Examples:
- # "hello".first # => "h"
- # "hello".first(2) # => "he"
- # "hello".first(10) # => "hello"
- def first(limit = 1)
- if limit == 0
- ''
- elsif limit >= size
- self
- else
- mb_chars[0...limit].to_s
- end
- end
-
- # Returns the last character of the string or the last +limit+ characters.
- #
- # Examples:
- # "hello".last # => "o"
- # "hello".last(2) # => "lo"
- # "hello".last(10) # => "hello"
- def last(limit = 1)
- if limit == 0
- ''
- elsif limit >= size
- self
- else
- mb_chars[(-limit)..-1].to_s
- end
- end
- else
- def at(position)
- self[position]
- end
+ def at(position)
+ self[position]
+ end
- def from(position)
- self[position..-1]
- end
+ def from(position)
+ self[position..-1]
+ end
- def to(position)
- self[0..position]
- end
+ def to(position)
+ self[0..position]
+ end
- def first(limit = 1)
- if limit == 0
- ''
- elsif limit >= size
- self
- else
- to(limit - 1)
- end
+ def first(limit = 1)
+ if limit == 0
+ ''
+ elsif limit >= size
+ self
+ else
+ to(limit - 1)
end
+ end
- def last(limit = 1)
- if limit == 0
- ''
- elsif limit >= size
- self
- else
- from(-limit)
- end
+ def last(limit = 1)
+ if limit == 0
+ ''
+ elsif limit >= size
+ self
+ else
+ from(-limit)
end
end
end
diff --git a/activesupport/lib/active_support/core_ext/string/conversions.rb b/activesupport/lib/active_support/core_ext/string/conversions.rb
index 0f8933b658..9084bbee32 100644
--- a/activesupport/lib/active_support/core_ext/string/conversions.rb
+++ b/activesupport/lib/active_support/core_ext/string/conversions.rb
@@ -1,54 +1,36 @@
-# encoding: utf-8
require 'date'
-require 'active_support/core_ext/time/publicize_conversion_methods'
require 'active_support/core_ext/time/calculations'
class String
- # Returns the codepoint of the first character of the string, assuming a
- # single-byte character encoding:
- #
- # "a".ord # => 97
- # "à".ord # => 224, in ISO-8859-1
- #
- # This method is defined in Ruby 1.8 for Ruby 1.9 forward compatibility on
- # these character encodings.
- #
- # <tt>ActiveSupport::Multibyte::Chars#ord</tt> is forward compatible with
- # Ruby 1.9 on UTF8 strings:
- #
- # "a".mb_chars.ord # => 97
- # "à".mb_chars.ord # => 224, in UTF8
- #
- # Note that the 224 is different in both examples. In ISO-8859-1 "à" is
- # represented as a single byte, 224. In UTF8 it is represented with two
- # bytes, namely 195 and 160, but its Unicode codepoint is 224. If we
- # call +ord+ on the UTF8 string "à" the return value will be 195. That is
- # not an error, because UTF8 is unsupported, the call itself would be
- # bogus.
- def ord
- self[0]
- end unless method_defined?(:ord)
-
- # +getbyte+ backport from Ruby 1.9
- alias_method :getbyte, :[] unless method_defined?(:getbyte)
-
# 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/encoding.rb b/activesupport/lib/active_support/core_ext/string/encoding.rb
index d4781bfe0c..dc635ed6a5 100644
--- a/activesupport/lib/active_support/core_ext/string/encoding.rb
+++ b/activesupport/lib/active_support/core_ext/string/encoding.rb
@@ -1,11 +1,8 @@
+require 'active_support/deprecation'
+
class String
- if defined?(Encoding) && "".respond_to?(:encode)
- def encoding_aware?
- true
- end
- else
- def encoding_aware?
- false
- end
+ def encoding_aware?
+ ActiveSupport::Deprecation.warn 'String#encoding_aware? is deprecated', caller
+ true
end
-end \ No newline at end of file
+end
diff --git a/activesupport/lib/active_support/core_ext/string/filters.rb b/activesupport/lib/active_support/core_ext/string/filters.rb
index d478ee0ef6..32a37296d5 100644
--- a/activesupport/lib/active_support/core_ext/string/filters.rb
+++ b/activesupport/lib/active_support/core_ext/string/filters.rb
@@ -22,28 +22,34 @@ 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 = {})
- text = self.dup
- options[:omission] ||= "..."
+ def truncate(truncate_at, options = {})
+ return dup unless length > truncate_at
- length_with_room_for_omission = length - options[:omission].mb_chars.length
- chars = text.mb_chars
- stop = options[:separator] ?
- (chars.rindex(options[:separator].mb_chars, 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
- (chars.length > length ? chars[0...stop] + options[:omission] : text).to_s
+ self[0...stop] + options[:omission]
end
end
diff --git a/activesupport/lib/active_support/core_ext/string/inflections.rb b/activesupport/lib/active_support/core_ext/string/inflections.rb
index 002688d6c0..049ffe7986 100644
--- a/activesupport/lib/active_support/core_ext/string/inflections.rb
+++ b/activesupport/lib/active_support/core_ext/string/inflections.rb
@@ -4,57 +4,83 @@ 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.
#
- # "post".pluralize # => "posts"
- # "octopus".pluralize # => "octopi"
- # "sheep".pluralize # => "sheep"
- # "words".pluralize # => "words"
- # "the blue mailman".pluralize # => "the blue mailmen"
- # "CamelOctopus".pluralize # => "CamelOctopi"
- def pluralize
- ActiveSupport::Inflector.pluralize(self)
+ # If the optional parameter +count+ is specified,
+ # the singular form will be returned if <tt>count == 1</tt>.
+ # 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"
+ def pluralize(count = nil)
+ if count == 1
+ self
+ else
+ ActiveSupport::Inflector.pluralize(self)
+ end
end
# 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
# +constantize+ tries to find a declared constant with the name specified
# in the string. It raises a NameError when the name is not in CamelCase
- # or is not initialized.
+ # or is not initialized. See ActiveSupport::Inflector.constantize
#
# Examples
- # "Module".constantize # => Module
- # "Class".constantize # => Class
+ # 'Module'.constantize # => Module
+ # 'Class'.constantize # => Class
+ # 'blargle'.constantize # => NameError: wrong constant name blargle
def constantize
ActiveSupport::Inflector.constantize(self)
end
+ # +safe_constantize+ tries to find a declared constant with the name specified
+ # in the string. It returns nil when the name is not in CamelCase
+ # or is not initialized. See ActiveSupport::Inflector.safe_constantize
+ #
+ # Examples
+ # 'Module'.safe_constantize # => Module
+ # 'Class'.safe_constantize # => Class
+ # 'blargle'.safe_constantize # => nil
+ def safe_constantize
+ ActiveSupport::Inflector.safe_constantize(self)
+ end
+
# By default, +camelize+ converts strings to UpperCamelCase. If the argument to camelize
# is set to <tt>:lower</tt> then camelize produces lowerCamelCase.
#
# +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
@@ -65,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
@@ -76,27 +102,42 @@ 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
ActiveSupport::Inflector.demodulize(self)
end
+ # Removes the rightmost segment from the constant expression in the string.
+ #
+ # 'Net::HTTP'.deconstantize # => "Net"
+ # '::Net::HTTP'.deconstantize # => "::Net"
+ # 'String'.deconstantize # => ""
+ # '::String'.deconstantize # => ""
+ # ''.deconstantize # => ""
+ #
+ # See also +demodulize+.
+ def deconstantize
+ ActiveSupport::Inflector.deconstantize(self)
+ end
+
# Replaces special characters in a string so that it may be used as part of a 'pretty' URL.
#
# ==== Examples
@@ -119,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
@@ -130,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
@@ -143,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
@@ -154,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/multibyte.rb b/activesupport/lib/active_support/core_ext/string/multibyte.rb
index aae1cfccf2..4e7824ad74 100644
--- a/activesupport/lib/active_support/core_ext/string/multibyte.rb
+++ b/activesupport/lib/active_support/core_ext/string/multibyte.rb
@@ -2,71 +2,55 @@
require 'active_support/multibyte'
class String
- if RUBY_VERSION >= "1.9"
- # == Multibyte proxy
- #
- # +mb_chars+ is a multibyte safe proxy for string methods.
- #
- # In Ruby 1.8 and older it creates and returns an instance of the ActiveSupport::Multibyte::Chars class which
- # encapsulates the original string. A Unicode safe version of all the String methods are defined on this proxy
- # class. If the proxy class doesn't respond to a certain method, it's forwarded to the encapsulated string.
- #
- # name = 'Claus Müller'
- # name.reverse # => "rell??M sualC"
- # name.length # => 13
- #
- # name.mb_chars.reverse.to_s # => "rellüM sualC"
- # name.mb_chars.length # => 12
- #
- # In Ruby 1.9 and newer +mb_chars+ returns +self+ because String is (mostly) encoding aware. This means that
- # it becomes easy to run one version of your code on multiple Ruby versions.
- #
- # == Method chaining
- #
- # All the methods on the Chars proxy which normally return a string will return a Chars object. This allows
- # method chaining on the result of any of these methods.
- #
- # name.mb_chars.reverse.length # => 12
- #
- # == Interoperability and configuration
- #
- # The Chars object tries to be as interchangeable with String objects as possible: sorting and comparing between
- # String and Char work like expected. The bang! methods change the internal string representation in the Chars
- # object. Interoperability problems can be resolved easily with a +to_s+ call.
- #
- # For more information about the methods defined on the Chars proxy see ActiveSupport::Multibyte::Chars. For
- # information about how to change the default Multibyte behavior see ActiveSupport::Multibyte.
- def mb_chars
- if ActiveSupport::Multibyte.proxy_class.consumes?(self)
- ActiveSupport::Multibyte.proxy_class.new(self)
- else
- self
- end
- end
-
- def is_utf8? #:nodoc
- case encoding
- when Encoding::UTF_8
- valid_encoding?
- when Encoding::ASCII_8BIT, Encoding::US_ASCII
- dup.force_encoding(Encoding::UTF_8).valid_encoding?
- else
- false
- end
- end
- else
- def mb_chars
- if ActiveSupport::Multibyte.proxy_class.wants?(self)
- ActiveSupport::Multibyte.proxy_class.new(self)
- else
- self
- end
+ # == Multibyte proxy
+ #
+ # +mb_chars+ is a multibyte safe proxy for string methods.
+ #
+ # In Ruby 1.8 and older it creates and returns an instance of the ActiveSupport::Multibyte::Chars class which
+ # encapsulates the original string. A Unicode safe version of all the String methods are defined on this proxy
+ # class. If the proxy class doesn't respond to a certain method, it's forwarded to the encapsulated string.
+ #
+ # name = 'Claus Müller'
+ # name.reverse # => "rell??M sualC"
+ # name.length # => 13
+ #
+ # name.mb_chars.reverse.to_s # => "rellüM sualC"
+ # name.mb_chars.length # => 12
+ #
+ # In Ruby 1.9 and newer +mb_chars+ returns +self+ because String is (mostly) encoding aware. This means that
+ # it becomes easy to run one version of your code on multiple Ruby versions.
+ #
+ # == Method chaining
+ #
+ # All the methods on the Chars proxy which normally return a string will return a Chars object. This allows
+ # method chaining on the result of any of these methods.
+ #
+ # name.mb_chars.reverse.length # => 12
+ #
+ # == Interoperability and configuration
+ #
+ # The Chars object tries to be as interchangeable with String objects as possible: sorting and comparing between
+ # String and Char work like expected. The bang! methods change the internal string representation in the Chars
+ # object. Interoperability problems can be resolved easily with a +to_s+ call.
+ #
+ # For more information about the methods defined on the Chars proxy see ActiveSupport::Multibyte::Chars. For
+ # information about how to change the default Multibyte behavior see ActiveSupport::Multibyte.
+ def mb_chars
+ if ActiveSupport::Multibyte.proxy_class.consumes?(self)
+ ActiveSupport::Multibyte.proxy_class.new(self)
+ else
+ self
end
+ end
- # Returns true if the string has UTF-8 semantics (a String used for purely byte resources is unlikely to have
- # them), returns false otherwise.
- def is_utf8?
- ActiveSupport::Multibyte::Chars.consumes?(self)
+ def is_utf8?
+ case encoding
+ when Encoding::UTF_8
+ valid_encoding?
+ when Encoding::ASCII_8BIT, Encoding::US_ASCII
+ dup.force_encoding(Encoding::UTF_8).valid_encoding?
+ else
+ false
end
end
end
diff --git a/activesupport/lib/active_support/core_ext/string/output_safety.rb b/activesupport/lib/active_support/core_ext/string/output_safety.rb
index 6d6c4912bb..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 = { '&' => '&amp;', '>' => '&gt;', '<' => '&lt;', '"' => '&quot;' }
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,14 +15,14 @@ class ERB
# <%=h @person.name %>
#
# ==== Example:
- # puts html_escape("is a > 0 & a < 10?")
+ # puts html_escape('is a > 0 & a < 10?')
# # => is a &gt; 0 &amp; a &lt; 10?
def html_escape(s)
s = s.to_s
if s.html_safe?
s
else
- s.gsub(/[&"><]/) { |special| HTML_ESCAPE[special] }.html_safe
+ s.encode(s.encoding, :xml => :attr)[1...-1].html_safe
end
end
@@ -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 &amp; 3')
+ # # => "1 &lt; 2 &amp; 3"
+ #
+ # html_escape_once('&lt;&lt; Accept & Checkout')
+ # # => "&lt;&lt; Accept &amp; 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"].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
@@ -136,28 +168,19 @@ module ActiveSupport #:nodoc:
coder.represent_scalar nil, to_str
end
- def to_yaml(*args)
- return super() if defined?(YAML::ENGINE) && !YAML::ENGINE.syck?
- to_str.to_yaml(*args)
- end
-
UNSAFE_STRING_METHODS.each do |unsafe_method|
- class_eval <<-EOT, __FILE__, __LINE__
- def #{unsafe_method}(*args, &block) # def gsub(*args, &block)
- to_str.#{unsafe_method}(*args, &block) # to_str.gsub(*args, &block)
- end # end
-
- def #{unsafe_method}!(*args) # def gsub!(*args)
- @dirty = true # @dirty = true
- super # super
- end # end
- EOT
- end
-
- protected
-
- def dirty?
- @dirty
+ if 'String'.respond_to?(unsafe_method)
+ class_eval <<-EOT, __FILE__, __LINE__ + 1
+ def #{unsafe_method}(*args, &block) # def capitalize(*args, &block)
+ to_str.#{unsafe_method}(*args, &block) # to_str.capitalize(*args, &block)
+ end # end
+
+ def #{unsafe_method}!(*args) # def capitalize!(*args)
+ @html_safe = false # @html_safe = false
+ super # super
+ end # end
+ EOT
+ end
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 a15a06d0e4..1434e186c3 100644
--- a/activesupport/lib/active_support/core_ext/time/calculations.rb
+++ b/activesupport/lib/active_support/core_ext/time/calculations.rb
@@ -1,22 +1,32 @@
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
def ===(other)
- other.is_a?(::Time)
+ super || (self == Time && other.is_a?(ActiveSupport::TimeWithZone))
end
# 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 : usec)
+ 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,35 +179,63 @@ class Time
def prev_month
months_ago(1)
end
+ alias_method :last_month, :prev_month
# Short-hand for months_since(1)
def next_month
months_since(1)
end
- # Returns a new Time representing the "start" of this week (Monday, 0:00)
- def beginning_of_week
- days_to_monday = wday!=0 ? wday-1 : 6
- (self - days_to_monday.days).midnight
+ # Returns number of days to start of this week, week starts on start_day (default is :monday).
+ def days_to_week_start(start_day = :monday)
+ 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
+
+ # Returns a new Time representing the "start" of this week, week starts on start_day (default is :monday, i.e. Monday, 0:00).
+ def beginning_of_week(start_day = :monday)
+ days_to_start = days_to_week_start(start_day)
+ (self - days_to_start.days).midnight
end
- alias :monday :beginning_of_week
alias :at_beginning_of_week :beginning_of_week
- # Returns a new Time representing the end of this week, (end of Sunday)
- def end_of_week
- days_to_sunday = wday!=0 ? 7-wday : 0
- (self + days_to_sunday.days).end_of_day
+ # Returns a new +Date+/+DateTime+ representing the start of this week. Week is
+ # assumed to start on a Monday. +DateTime+ objects have their time set to 0:00.
+ def monday
+ beginning_of_week
+ end
+
+ # Returns a new Time representing the end of this week, week starts on start_day (default is :monday, i.e. end of Sunday).
+ def end_of_week(start_day = :monday)
+ days_to_end = 6 - days_to_week_start(start_day)
+ (self + days_to_end.days).end_of_day
end
alias :at_end_of_week :end_of_week
- # Returns a new Time representing the start of the given day in the previous week (default is Monday).
+ # Returns a new +Date+/+DateTime+ representing the end of this week. Week is
+ # assumed to start on a Monday. +DateTime+ objects have their time set to 23:59:59.
+ def sunday
+ end_of_week
+ end
+
+ # 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).
+ # 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)
@@ -197,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)
@@ -211,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
@@ -235,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
@@ -254,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.
@@ -308,8 +380,23 @@ 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
+
+ # Layers additional behavior on Time#eql? so that ActiveSupport::TimeWithZone instances
+ # can be eql? to an equivalent Time
+ def eql_with_coercion(other)
+ # if other is an ActiveSupport::TimeWithZone, coerce a Time instance from it so we can do eql? comparison
+ other = other.comparable_time if other.respond_to?(:comparable_time)
+ eql_without_coercion(other)
+ 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 d9d5e02778..4f852fd780 100644
--- a/activesupport/lib/active_support/core_ext/time/conversions.rb
+++ b/activesupport/lib/active_support/core_ext/time/conversions.rb
@@ -1,16 +1,21 @@
require 'active_support/inflector/methods'
-require 'active_support/core_ext/time/publicize_conversion_methods'
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.
@@ -35,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]
@@ -54,10 +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
-
- # 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)
end
diff --git a/activesupport/lib/active_support/core_ext/time/marshal.rb b/activesupport/lib/active_support/core_ext/time/marshal.rb
index 457d3f5b62..1bf622d6a6 100644
--- a/activesupport/lib/active_support/core_ext/time/marshal.rb
+++ b/activesupport/lib/active_support/core_ext/time/marshal.rb
@@ -1,30 +1,3 @@
-# Pre-1.9 versions of Ruby have a bug with marshaling Time instances, where utc instances are
-# unmarshalled in the local zone, instead of utc. We're layering behavior on the _dump and _load
-# methods so that utc instances can be flagged on dump, and coerced back to utc on load.
-if !Marshal.load(Marshal.dump(Time.now.utc)).utc?
- class Time
- class << self
- alias_method :_load_without_utc_flag, :_load
- def _load(marshaled_time)
- time = _load_without_utc_flag(marshaled_time)
- time.instance_eval do
- if defined?(@marshal_with_utc_coercion)
- val = remove_instance_variable("@marshal_with_utc_coercion")
- end
- val ? utc : self
- end
- end
- end
-
- alias_method :_dump_without_utc_flag, :_dump
- def _dump(*args)
- obj = dup
- obj.instance_variable_set('@marshal_with_utc_coercion', utc?)
- obj._dump_without_utc_flag(*args)
- end
- end
-end
-
# Ruby 1.9.2 adds utc_offset and zone to Time, but marshaling only
# preserves utc_offset. Preserve zone also, even though it may not
# work in some edge cases.
diff --git a/activesupport/lib/active_support/core_ext/time/publicize_conversion_methods.rb b/activesupport/lib/active_support/core_ext/time/publicize_conversion_methods.rb
deleted file mode 100644
index e1878d3c20..0000000000
--- a/activesupport/lib/active_support/core_ext/time/publicize_conversion_methods.rb
+++ /dev/null
@@ -1,10 +0,0 @@
-require 'date'
-
-class Time
- # Ruby 1.8-cvs and early 1.9 series define private Time#to_date
- %w(to_date to_datetime).each do |method|
- if private_instance_methods.include?(method) || private_instance_methods.include?(method.to_sym)
- public method
- end
- end
-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 ee991e3439..bfe0832b37 100644
--- a/activesupport/lib/active_support/core_ext/uri.rb
+++ b/activesupport/lib/active_support/core_ext/uri.rb
@@ -1,22 +1,18 @@
# encoding: utf-8
-if RUBY_VERSION >= '1.9'
- require 'uri'
+require 'uri'
+str = "\xE6\x97\xA5\xE6\x9C\xAC\xE8\xAA\x9E" # Ni-ho-nn-go in UTF-8, means Japanese.
+parser = URI::Parser.new
- str = "\xE6\x97\xA5\xE6\x9C\xAC\xE8\xAA\x9E" # Ni-ho-nn-go in UTF-8, means Japanese.
-
- parser = URI::Parser.new
-
- unless str == parser.unescape(parser.escape(str))
- URI::Parser.class_eval do
- remove_method :unescape
- def unescape(str, escaped = /%[a-fA-F\d]{2}/)
- # TODO: Are we actually sure that ASCII == UTF-8?
- # YK: My initial experiments say yes, but let's be sure please
- enc = str.encoding
- enc = Encoding::UTF_8 if enc == Encoding::US_ASCII
- str.gsub(escaped) { [$&[1, 2].hex].pack('C') }.force_encoding(enc)
- end
+unless str == parser.unescape(parser.escape(str))
+ URI::Parser.class_eval do
+ remove_method :unescape
+ def unescape(str, escaped = /%[a-fA-F\d]{2}/)
+ # TODO: Are we actually sure that ASCII == UTF-8?
+ # YK: My initial experiments say yes, but let's be sure please
+ enc = str.encoding
+ enc = Encoding::UTF_8 if enc == Encoding::US_ASCII
+ str.gsub(escaped) { [$&[1, 2].hex].pack('C') }.force_encoding(enc)
end
end
end
@@ -24,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 3f6c93e860..745a131524 100644
--- a/activesupport/lib/active_support/dependencies.rb
+++ b/activesupport/lib/active_support/dependencies.rb
@@ -5,6 +5,7 @@ require 'active_support/core_ext/module/aliasing'
require 'active_support/core_ext/module/attribute_accessors'
require 'active_support/core_ext/module/introspection'
require 'active_support/core_ext/module/anonymous'
+require 'active_support/core_ext/module/qualified_const'
require 'active_support/core_ext/object/blank'
require 'active_support/core_ext/load_error'
require 'active_support/core_ext/name_error'
@@ -70,14 +71,24 @@ module ActiveSupport #:nodoc:
#
# This is handled by walking back up the watch stack and adding the constants
# found by child.rb to the list of original constants in parent.rb
- class WatchStack < Hash
+ class WatchStack
+ include Enumerable
+
# @watching is a stack of lists of constants being watched. For instance,
# if parent.rb is autoloaded, the stack will look like [[Object]]. If parent.rb
# then requires namespace/child.rb, the stack will look like [[Object], [Namespace]].
def initialize
@watching = []
- super { |h,k| h[k] = [] }
+ @stack = Hash.new { |h,k| h[k] = [] }
+ end
+
+ def each(&block)
+ @stack.each(&block)
+ end
+
+ def watching?
+ !@watching.empty?
end
# return a list of new constants found since the last call to watch_namespaces
@@ -88,20 +99,20 @@ module ActiveSupport #:nodoc:
@watching.last.each do |namespace|
# Retrieve the constants that were present under the namespace when watch_namespaces
# was originally called
- original_constants = self[namespace].last
+ original_constants = @stack[namespace].last
mod = Inflector.constantize(namespace) if Dependencies.qualified_const_defined?(namespace)
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
# element of self[Object] will be an Array of the constants that were present
# before parent.rb was required. The second element will be an Array of the
# constants that were present before child.rb was required.
- self[namespace].each do |namespace_constants|
+ @stack[namespace].each do |namespace_constants|
namespace_constants.concat(new_constants)
end
@@ -118,20 +129,19 @@ 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
- self[module_name] << original_constants
+ @stack[module_name] << original_constants
+ module_name
end
- @watching << watching
end
+ private
def pop_modules(modules)
- modules.each { |mod| self[mod].pop }
+ modules.each { |mod| @stack[mod].pop }
end
end
@@ -218,8 +228,8 @@ module ActiveSupport #:nodoc:
end
def load_dependency(file)
- if Dependencies.load?
- Dependencies.new_constants_in(Object) { yield }.presence
+ if Dependencies.load? && ActiveSupport::Dependencies.constant_watch_stack.watching?
+ Dependencies.new_constants_in(Object) { yield }
else
yield
end
@@ -228,12 +238,16 @@ module ActiveSupport #:nodoc:
raise
end
- def load(file, *)
- load_dependency(file) { super }
+ def load(file, wrap = false)
+ result = false
+ load_dependency(file) { result = super }
+ result
end
- def require(file, *)
- load_dependency(file) { super }
+ def require(file)
+ result = false
+ load_dependency(file) { result = super }
+ result
end
# Mark the given constant as unloadable. Unloadable constants are removed each
@@ -349,29 +363,16 @@ module ActiveSupport #:nodoc:
# Record history *after* loading so first load gets warnings.
history << expanded
- return result
+ result
end
# Is the provided constant path defined?
def qualified_const_defined?(path)
- names = path.sub(/^::/, '').to_s.split('::')
-
- names.inject(Object) do |mod, name|
- return false unless local_const_defined?(mod, name)
- mod.const_get name
- end
+ Object.qualified_const_defined?(path.sub(/^::/, ''), false)
end
- if Module.method(:const_defined?).arity == 1
- # Does this module define this constant?
- # Wrapper to accommodate changing Module#const_defined? in Ruby 1.9
- def local_const_defined?(mod, const)
- mod.const_defined?(const)
- end
- else
- def local_const_defined?(mod, const) #:nodoc:
- mod.const_defined?(const, false)
- end
+ def local_const_defined?(mod, const) #:nodoc:
+ mod.const_defined?(const, false)
end
# Given +path+, a filesystem path to a ruby file, return an array of constant
@@ -417,11 +418,12 @@ module ActiveSupport #:nodoc:
end
def load_once_path?(path)
- autoload_once_paths.any? { |base| path.starts_with? base }
+ # to_s works around a ruby1.9 issue where #starts_with?(Pathname) will always return false
+ autoload_once_paths.any? { |base| path.starts_with? base.to_s }
end
# Attempt to autoload the provided module name by searching for a directory
- # matching the expect path suffix. If found, the module is created and assigned
+ # matching the expected path suffix. If found, the module is created and assigned
# to +into+'s constants with the name +const_name+. Provided that the directory
# was loaded from a reloadable base path, it is added to the set of constants
# that are to be unloaded.
@@ -430,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
@@ -454,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.
@@ -501,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
@@ -520,7 +522,7 @@ module ActiveSupport #:nodoc:
class ClassCache
def initialize
- @store = Hash.new { |h, k| h[k] = Inflector.constantize(k) }
+ @store = Hash.new
end
def empty?
@@ -531,23 +533,21 @@ module ActiveSupport #:nodoc:
@store.key?(key)
end
- def []=(key, value)
- return unless key.respond_to?(:name)
-
- raise(ArgumentError, 'anonymous classes cannot be cached') if key.name.blank?
-
- @store[key.name] = value
+ def get(key)
+ key = key.name if key.respond_to?(:name)
+ @store[key] ||= Inflector.constantize(key)
end
+ alias :[] :get
- def [](key)
+ def safe_get(key)
key = key.name if key.respond_to?(:name)
-
- @store[key]
+ @store[key] ||= Inflector.safe_constantize(key)
end
- alias :get :[]
- def store(name)
- self[name] = name
+ def store(klass)
+ return self unless klass.respond_to?(:name)
+ raise(ArgumentError, 'anonymous classes cannot be cached') if klass.name.empty?
+ @store[klass.name] = klass
self
end
@@ -564,10 +564,17 @@ module ActiveSupport #:nodoc:
end
# Get the reference for class named +name+.
+ # Raises an exception if referenced class does not exist.
def constantize(name)
Reference.get(name)
end
+ # Get the reference for class named +name+ if one exists.
+ # Otherwise returns nil.
+ def safe_constantize(name)
+ Reference.safe_get(name)
+ end
+
# Determine if the given constant has been automatically loaded.
def autoloaded?(desc)
# No name => anonymous module.
@@ -588,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
@@ -619,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).
@@ -651,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 f9505a247c..73b6a42505 100644
--- a/activesupport/lib/active_support/deprecation/behaviors.rb
+++ b/activesupport/lib/active_support/deprecation/behaviors.rb
@@ -1,5 +1,4 @@
require "active_support/notifications"
-require "active_support/core_ext/array/wrap"
module ActiveSupport
module Deprecation
@@ -14,12 +13,19 @@ module ActiveSupport
# Sets the behavior to the specified value. Can be a single value or an array.
#
+ # Available behaviors:
+ #
+ # [+:stderr+] Print deprecations to +$stderror+
+ # [+:log+] Send to +Rails.logger+
+ # [+:notify+] Instrument using +ActiveSupport::Notifications+
+ # [+:silence+] Do nothing
+ #
# Examples
#
# ActiveSupport::Deprecation.behavior = :stderr
# ActiveSupport::Deprecation.behavior = [:stderr, :log]
def behavior=(behavior)
- @behavior = Array.wrap(behavior).map { |b| DEFAULT_BEHAVIORS[b] || b }
+ @behavior = Array(behavior).map { |b| DEFAULT_BEHAVIORS[b] || b }
end
end
@@ -34,16 +40,17 @@ module ActiveSupport
if defined?(Rails) && Rails.logger
Rails.logger
else
- require 'logger'
- Logger.new($stderr)
+ require 'active_support/logger'
+ ActiveSupport::Logger.new($stderr)
end
logger.warn message
logger.debug callstack.join("\n ") if debug
},
: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/duration.rb b/activesupport/lib/active_support/duration.rb
index 89b0923882..00c67a470d 100644
--- a/activesupport/lib/active_support/duration.rb
+++ b/activesupport/lib/active_support/duration.rb
@@ -10,7 +10,6 @@ module ActiveSupport
# 1.month.ago # equivalent to Time.now.advance(:months => -1)
class Duration < BasicObject
attr_accessor :value, :parts
- delegate :duplicable?, :to => :value # required when using ActiveSupport's BasicObject on 1.8
def initialize(value, parts) #:nodoc:
@value, @parts = value, parts
diff --git a/activesupport/lib/active_support/file_update_checker.rb b/activesupport/lib/active_support/file_update_checker.rb
index f76ddff038..8860636168 100644
--- a/activesupport/lib/active_support/file_update_checker.rb
+++ b/activesupport/lib/active_support/file_update_checker.rb
@@ -1,8 +1,25 @@
module ActiveSupport
- # This class is responsible to track files and invoke the given block
- # whenever one of these files are changed. For example, this class
- # is used by Rails to reload the I18n framework whenever they are
- # changed upon a new request.
+ # \FileUpdateChecker specifies the API used by Rails to watch files
+ # and control reloading. The API depends on four methods:
+ #
+ # * +initialize+ which expects two parameters and one block as
+ # described below;
+ #
+ # * +updated?+ which returns a boolean if there were updates in
+ # the filesystem or not;
+ #
+ # * +execute+ which executes the given block on initialization
+ # and updates the latest watched files and timestamp;
+ #
+ # * +execute_if_updated+ which just executes the block if it was updated;
+ #
+ # After initialization, a call to +execute_if_updated+ must execute
+ # the block only if there was really a change in the filesystem.
+ #
+ # == Examples
+ #
+ # This class is used by Rails to reload the I18n framework whenever
+ # they are changed upon a new request.
#
# i18n_reloader = ActiveSupport::FileUpdateChecker.new(paths) do
# I18n.reload!
@@ -13,24 +30,109 @@ module ActiveSupport
# end
#
class FileUpdateChecker
- attr_reader :paths, :last_update_at
-
- def initialize(paths, calculate=false, &block)
- @paths = paths
+ # It accepts two parameters on initialization. The first is an array
+ # of files and the second is an optional hash of directories. The hash must
+ # have directories as keys and the value is an array of extensions to be
+ # watched under that directory.
+ #
+ # This method must also receive a block that will be called once a path changes.
+ #
+ # == Implementation details
+ #
+ # 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 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.
+ def initialize(files, dirs={}, &block)
+ @files = files
+ @glob = compile_glob(dirs)
@block = block
- @last_update_at = calculate ? updated_at : nil
+
+ @watched = nil
+ @updated_at = nil
+
+ @last_watched = watched
+ @last_update_at = updated_at(@last_watched)
end
- def updated_at
- paths.map { |path| File.mtime(path) }.max
+ # 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_watched = watched
+ if @last_watched.size != current_watched.size
+ @watched = current_watched
+ true
+ else
+ 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 latest watched files and timestamp.
+ def execute
+ @last_watched = watched
+ @last_update_at = updated_at(@last_watched)
+ @block.call
+ ensure
+ @watched = nil
+ @updated_at = nil
+ end
+
+ # Execute the block given if updated.
def execute_if_updated
- current_update_at = self.updated_at
- if @last_update_at != current_update_at
- @last_update_at = current_update_at
- @block.call
+ if updated?
+ execute
+ true
+ else
+ false
end
end
+
+ private
+
+ def watched
+ @watched || begin
+ all = @files.select { |f| File.exists?(f) }
+ all.concat(Dir[@glob]) if @glob
+ all
+ end
+ end
+
+ 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.map do |key, value|
+ "#{escape(key)}/**/*#{compile_ext(value)}"
+ end
+ "{#{globs.join(",")}}"
+ end
+
+ def escape(key)
+ key.gsub(',','\,')
+ end
+
+ def compile_ext(array)
+ array = Array(array)
+ return if array.empty?
+ ".{#{array.join(",")}}"
+ end
end
end
diff --git a/activesupport/lib/active_support/gzip.rb b/activesupport/lib/active_support/gzip.rb
index 9651f02c73..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.
@@ -8,7 +7,7 @@ module ActiveSupport
class Stream < StringIO
def initialize(*)
super
- set_encoding "BINARY" if "".encoding_aware?
+ set_encoding "BINARY"
end
def close; rewind; end
end
diff --git a/activesupport/lib/active_support/hash_with_indifferent_access.rb b/activesupport/lib/active_support/hash_with_indifferent_access.rb
index 59ffd24698..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
@@ -16,6 +15,10 @@ module ActiveSupport
dup
end
+ def nested_under_indifferent_access
+ self
+ end
+
def initialize(constructor = {})
if constructor.is_a?(Hash)
super()
@@ -39,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)
@@ -112,7 +119,7 @@ module ActiveSupport
end
end
- # Merges the instantized and the specified hashes together, giving precedence to the values from the second hash
+ # Merges the instantized and the specified hashes together, giving precedence to the values from the second hash.
# Does not overwrite the existing hash.
def merge(hash)
self.dup.update(hash)
diff --git a/activesupport/lib/active_support/i18n_railtie.rb b/activesupport/lib/active_support/i18n_railtie.rb
index a25e951080..bbeb8d82c6 100644
--- a/activesupport/lib/active_support/i18n_railtie.rb
+++ b/activesupport/lib/active_support/i18n_railtie.rb
@@ -1,5 +1,4 @@
require "active_support"
-require "rails"
require "active_support/file_update_checker"
require "active_support/core_ext/array/wrap"
@@ -11,14 +10,19 @@ module I18n
config.i18n.fallbacks = ActiveSupport::OrderedOptions.new
def self.reloader
- @reloader ||= ActiveSupport::FileUpdateChecker.new([]){ I18n.reload! }
+ @reloader ||= ActiveSupport::FileUpdateChecker.new(reloader_paths){ I18n.reload! }
+ end
+
+ def self.reloader_paths
+ @reloader_paths ||= []
end
# Add <tt>I18n::Railtie.reloader</tt> to ActionDispatch callbacks. Since, at this
# point, no path was added to the reloader, I18n.reload! is not triggered
# on to_prepare callbacks. This will only happen on the config.after_initialize
# callback below.
- initializer "i18n.callbacks" do
+ initializer "i18n.callbacks" do |app|
+ app.reloaders << I18n::Railtie.reloader
ActionDispatch::Reloader.to_prepare do
I18n::Railtie.reloader.execute_if_updated
end
@@ -38,6 +42,8 @@ module I18n
protected
+ @i18n_inited = false
+
# Setup i18n configuration
def self.initialize_i18n(app)
return if @i18n_inited
@@ -57,8 +63,8 @@ module I18n
init_fallbacks(fallbacks) if fallbacks && validate_fallbacks(fallbacks)
- reloader.paths.concat I18n.load_path
- reloader.execute_if_updated
+ reloader_paths.concat I18n.load_path
+ reloader.execute
@i18n_inited = true
end
diff --git a/activesupport/lib/active_support/inflections.rb b/activesupport/lib/active_support/inflections.rb
index daf2a1e1d9..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 423b5abd20..61876d89a9 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
@@ -21,14 +23,7 @@ module ActiveSupport
# "words".pluralize # => "words"
# "CamelOctopus".pluralize # => "CamelOctopi"
def pluralize(word)
- result = word.to_s.dup
-
- if word.empty? || inflections.uncountables.include?(result.downcase)
- result
- else
- inflections.plurals.each { |(rule, replacement)| break if result.gsub!(rule, replacement) }
- result
- end
+ apply_inflections(word, inflections.plurals)
end
# The reverse of +pluralize+, returns the singular form of a word in a string.
@@ -40,14 +35,7 @@ module ActiveSupport
# "word".singularize # => "word"
# "CamelOctopi".singularize # => "CamelOctopus"
def singularize(word)
- result = word.to_s.dup
-
- if inflections.uncountables.any? { |inflection| result =~ /\b(#{inflection})\Z/i }
- result
- else
- inflections.singulars.each { |(rule, replacement)| break if result.gsub!(rule, replacement) }
- result
- end
+ apply_inflections(word, inflections.singulars)
end
# By default, +camelize+ converts strings to UpperCamelCase. If the argument to +camelize+
@@ -56,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:
@@ -80,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:
@@ -89,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')
@@ -106,9 +94,12 @@ module ActiveSupport
# "author_id" # => "Author"
def humanize(lower_case_and_underscored_word)
result = lower_case_and_underscored_word.to_s.dup
- inflections.humans.each { |(rule, replacement)| break if result.gsub!(rule, replacement) }
+ inflections.humans.each { |(rule, replacement)| break if result.sub!(rule, replacement) }
result.gsub!(/_id$/, "")
- result.gsub(/(_)?([a-z\d]*)/i) { "#{$1 && ' '}#{inflections.acronyms[$2] || $2.downcase}" }.gsub(/^\w/) { $&.upcase }
+ result.tr!('_', ' ')
+ result.gsub(/([a-z\d]*)/i) { |match|
+ "#{inflections.acronyms[match] || match.downcase}"
+ }.gsub(/^\w/) { $&.upcase }
end
# Capitalizes all the words and replaces some characters in the string to create
@@ -123,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
@@ -155,18 +146,37 @@ 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.
+ # Removes the module part from the expression in the string:
#
- # Examples:
# "ActiveRecord::CoreExtensions::String::Inflections".demodulize # => "Inflections"
# "Inflections".demodulize # => "Inflections"
- def demodulize(class_name_in_module)
- class_name_in_module.to_s.gsub(/^.*::/, '')
+ #
+ # See also +deconstantize+.
+ def demodulize(path)
+ path = path.to_s
+ if i = path.rindex('::')
+ path[(i+2)..-1]
+ else
+ path
+ end
+ end
+
+ # Removes the rightmost segment from the constant expression in the string:
+ #
+ # "Net::HTTP".deconstantize # => "Net"
+ # "::Net::HTTP".deconstantize # => "::Net"
+ # "String".deconstantize # => ""
+ # "::String".deconstantize # => ""
+ # "".deconstantize # => ""
+ #
+ # See also +demodulize+.
+ def deconstantize(path)
+ path.to_s[0...(path.rindex('::') || 0)] # implementation based on the one in facets' Module#spacename
end
# Creates a foreign key name from a class name.
@@ -181,46 +191,85 @@ module ActiveSupport
underscore(demodulize(class_name)) + (separate_class_name_and_id_with_underscore ? "_id" : "id")
end
- # Ruby 1.9 introduces an inherit argument for Module#const_get and
- # #const_defined? and changes their default behavior.
- if Module.method(:const_get).arity == 1
- # Tries to find a constant with the name specified in the argument string:
- #
- # "Module".constantize # => Module
- # "Test::Unit".constantize # => Test::Unit
- #
- # The name is assumed to be the one of a top-level constant, no matter whether
- # it starts with "::" or not. No lexical context is taken into account:
- #
- # C = 'outside'
- # module M
- # C = 'inside'
- # C # => 'inside'
- # "C".constantize # => 'outside', same as ::C
- # end
- #
- # NameError is raised when the name is not in CamelCase or the constant is
- # unknown.
- def constantize(camel_cased_word)
- names = camel_cased_word.split('::')
- names.shift if names.empty? || names.first.empty?
-
- constant = Object
- names.each do |name|
- constant = constant.const_defined?(name) ? constant.const_get(name) : constant.const_missing(name)
- end
- constant
+ # Tries to find a constant with the name specified in the argument string:
+ #
+ # "Module".constantize # => Module
+ # "Test::Unit".constantize # => Test::Unit
+ #
+ # The name is assumed to be the one of a top-level constant, no matter whether
+ # it starts with "::" or not. No lexical context is taken into account:
+ #
+ # C = 'outside'
+ # module M
+ # C = 'inside'
+ # C # => 'inside'
+ # "C".constantize # => 'outside', same as ::C
+ # end
+ #
+ # NameError is raised when the name is not in CamelCase or the constant is
+ # unknown.
+ def constantize(camel_cased_word) #:nodoc:
+ names = camel_cased_word.split('::')
+ names.shift if names.empty? || names.first.empty?
+
+ names.inject(Object) do |constant, name|
+ constant.const_get(name, false)
end
- else
- def constantize(camel_cased_word) #:nodoc:
- 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)
+ end
+
+ # Tries to find a constant with the name specified in the argument string:
+ #
+ # "Module".safe_constantize # => Module
+ # "Test::Unit".safe_constantize # => Test::Unit
+ #
+ # The name is assumed to be the one of a top-level constant, no matter whether
+ # it starts with "::" or not. No lexical context is taken into account:
+ #
+ # C = 'outside'
+ # module M
+ # C = 'inside'
+ # C # => 'inside'
+ # "C".safe_constantize # => 'outside', same as ::C
+ # end
+ #
+ # nil is returned when the name is not in CamelCase or the constant (or part of it) is
+ # unknown.
+ #
+ # "blargle".safe_constantize # => nil
+ # "UnknownModule".safe_constantize # => nil
+ # "UnknownModule::Foo::Bar".safe_constantize # => nil
+ #
+ def safe_constantize(camel_cased_word)
+ begin
+ constantize(camel_cased_word)
+ rescue NameError => e
+ 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
- constant
end
end
@@ -235,15 +284,35 @@ module ActiveSupport
# ordinalize(-11) # => "-11th"
# ordinalize(-1021) # => "-1021st"
def ordinalize(number)
- if (11..13).include?(number.to_i.abs % 100)
- "#{number}th"
+ "#{number}#{ordinal(number)}"
+ end
+
+ private
+
+ # Mount a regular expression that will match part by part of the constant.
+ # For instance, Foo::Bar::Baz will generate Foo(::Bar(::Baz)?)?
+ def const_regexp(camel_cased_word) #:nodoc:
+ parts = camel_cased_word.split("::")
+ last = parts.pop
+
+ parts.reverse.inject(last) do |acc, part|
+ part.empty? ? acc : "#{part}(::#{acc})?"
+ end
+ end
+
+ # Applies inflection rules for +singularize+ and +pluralize+.
+ #
+ # Examples:
+ # apply_inflections("post", inflections.plurals) # => "posts"
+ # apply_inflections("posts", inflections.singulars) # => "post"
+ def apply_inflections(word, rules)
+ result = word.to_s.dup
+
+ if word.empty? || inflections.uncountables.include?(result.downcase[/\b\w+\Z/])
+ result
else
- case number.to_i.abs % 10
- when 1; "#{number}st"
- when 2; "#{number}nd"
- when 3; "#{number}rd"
- else "#{number}th"
- end
+ rules.each { |(rule, replacement)| break if result.sub!(rule, replacement) }
+ result
end
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 a7a09d5bbe..e0dee0e072 100644
--- a/activesupport/lib/active_support/json/encoding.rb
+++ b/activesupport/lib/active_support/json/encoding.rb
@@ -1,11 +1,9 @@
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
-require 'active_support/core_ext/array/wrap'
require 'active_support/core_ext/hash/except'
require 'active_support/core_ext/hash/slice'
require 'active_support/core_ext/object/instance_variables'
@@ -38,7 +36,7 @@ module ActiveSupport
attr_reader :options
def initialize(options = nil)
- @options = options
+ @options = options || {}
@seen = Set.new
end
@@ -50,16 +48,16 @@ module ActiveSupport
end
# like encode, but only calls as_json, without encoding to string
- def as_json(value)
+ def as_json(value, use_options = true)
check_for_circular_references(value) do
- value.as_json(options_for(value))
+ use_options ? value.as_json(options_for(value)) : value.as_json
end
end
def options_for(value)
if value.is_a?(Array) || value.is_a?(Hash)
# hashes and arrays need to get encoder in the options, so that they can detect circular references
- (options || {}).merge(:encoder => self)
+ options.merge(:encoder => self)
else
options
end
@@ -119,9 +117,7 @@ module ActiveSupport
end
def escape(string)
- if string.respond_to?(:force_encoding)
- string = string.encode(::Encoding::UTF_8, :undef => :replace).force_encoding(::Encoding::BINARY)
- end
+ string = string.encode(::Encoding::UTF_8, :undef => :replace).force_encoding(::Encoding::BINARY)
json = string.
gsub(escape_regex) { |s| ESCAPED_CHARS[s] }.
gsub(/([\xC0-\xDF][\x80-\xBF]|
@@ -130,7 +126,7 @@ module ActiveSupport
s.unpack("U*").pack("n*").unpack("H*")[0].gsub(/.{4}/n, '\\\\u\&')
}
json = %("#{json}")
- json.force_encoding(::Encoding::UTF_8) if json.respond_to?(:force_encoding)
+ json.force_encoding(::Encoding::UTF_8)
json
end
end
@@ -151,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
@@ -212,11 +208,15 @@ module Enumerable
end
end
+class Range
+ def as_json(options = nil) to_s end #:nodoc:
+end
+
class Array
def as_json(options = nil) #:nodoc:
# use encoder as a proxy to call as_json on all elements, to protect from circular references
encoder = options && options[:encoder] || ActiveSupport::JSON::Encoding::Encoder.new(options)
- map { |v| encoder.as_json(v) }
+ map { |v| encoder.as_json(v, options) }
end
def encode_json(encoder) #:nodoc:
@@ -230,9 +230,9 @@ class Hash
# create a subset of the hash by applying :only or :except
subset = if options
if attrs = options[:only]
- slice(*Array.wrap(attrs))
+ slice(*Array(attrs))
elsif attrs = options[:except]
- except(*Array.wrap(attrs))
+ except(*Array(attrs))
else
self
end
@@ -242,8 +242,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)] }]
+ 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/log_subscriber/test_helper.rb b/activesupport/lib/active_support/log_subscriber/test_helper.rb
index 3e54134c5c..7b7fc81e6c 100644
--- a/activesupport/lib/active_support/log_subscriber/test_helper.rb
+++ b/activesupport/lib/active_support/log_subscriber/test_helper.rb
@@ -18,8 +18,8 @@ module ActiveSupport
# Developer.all
# wait
# assert_equal 1, @logger.logged(:debug).size
- # assert_match /Developer Load/, @logger.logged(:debug).last
- # assert_match /SELECT \* FROM "developers"/, @logger.logged(:debug).last
+ # assert_match(/Developer Load/, @logger.logged(:debug).last)
+ # assert_match(/SELECT \* FROM "developers"/, @logger.logged(:debug).last)
# end
# end
#
@@ -50,7 +50,7 @@ module ActiveSupport
end
class MockLogger
- include ActiveSupport::BufferedLogger::Severity
+ include ActiveSupport::Logger::Severity
attr_reader :flush_count
attr_accessor :level
@@ -73,7 +73,7 @@ module ActiveSupport
@flush_count += 1
end
- ActiveSupport::BufferedLogger::Severity.constants.each do |severity|
+ ActiveSupport::Logger::Severity.constants.each do |severity|
class_eval <<-EOT, __FILE__, __LINE__ + 1
def #{severity.downcase}?
#{severity} >= @level
diff --git a/activesupport/lib/active_support/logger.rb b/activesupport/lib/active_support/logger.rb
new file mode 100644
index 0000000000..d055767eab
--- /dev/null
+++ b/activesupport/lib/active_support/logger.rb
@@ -0,0 +1,53 @@
+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
+ end
+
+ # Simple formatter which only displays the message.
+ class SimpleFormatter < ::Logger::Formatter
+ # This method is invoked when a log event occurs
+ def call(severity, timestamp, progname, msg)
+ "#{String === msg ? msg : msg.inspect}\n"
+ end
+ end
+ end
+end
diff --git a/activesupport/lib/active_support/memoizable.rb b/activesupport/lib/active_support/memoizable.rb
deleted file mode 100644
index 4c67676ad5..0000000000
--- a/activesupport/lib/active_support/memoizable.rb
+++ /dev/null
@@ -1,116 +0,0 @@
-require 'active_support/core_ext/kernel/singleton_class'
-require 'active_support/core_ext/module/aliasing'
-require 'active_support/deprecation'
-
-module ActiveSupport
- module Memoizable
- def self.extended(base)
- ActiveSupport::Deprecation.warn "ActiveSupport::Memoizable is deprecated and will be removed in future releases," \
- "simply use Ruby memoization pattern instead.", caller
- super
- end
-
- def self.memoized_ivar_for(symbol)
- "@_memoized_#{symbol.to_s.sub(/\?\Z/, '_query').sub(/!\Z/, '_bang')}".to_sym
- end
-
- module InstanceMethods
- def self.included(base)
- base.class_eval do
- unless base.method_defined?(:freeze_without_memoizable)
- alias_method_chain :freeze, :memoizable
- end
- end
- end
-
- def freeze_with_memoizable
- memoize_all unless frozen?
- freeze_without_memoizable
- end
-
- def memoize_all
- prime_cache ".*"
- end
-
- def unmemoize_all
- flush_cache ".*"
- end
-
- def prime_cache(*syms)
- syms.each do |sym|
- methods.each do |m|
- if m.to_s =~ /^_unmemoized_(#{sym})/
- if method(m).arity == 0
- __send__($1)
- else
- ivar = ActiveSupport::Memoizable.memoized_ivar_for($1)
- instance_variable_set(ivar, {})
- end
- end
- end
- end
- end
-
- def flush_cache(*syms)
- syms.each do |sym|
- (methods + private_methods + protected_methods).each do |m|
- if m.to_s =~ /^_unmemoized_(#{sym.to_s.gsub(/\?\Z/, '\?')})/
- ivar = ActiveSupport::Memoizable.memoized_ivar_for($1)
- instance_variable_get(ivar).clear if instance_variable_defined?(ivar)
- end
- end
- end
- end
- end
-
- def memoize(*symbols)
- symbols.each do |symbol|
- original_method = :"_unmemoized_#{symbol}"
- memoized_ivar = ActiveSupport::Memoizable.memoized_ivar_for(symbol)
-
- class_eval <<-EOS, __FILE__, __LINE__ + 1
- include InstanceMethods # include InstanceMethods
- #
- if method_defined?(:#{original_method}) # if method_defined?(:_unmemoized_mime_type)
- raise "Already memoized #{symbol}" # raise "Already memoized mime_type"
- end # end
- alias #{original_method} #{symbol} # alias _unmemoized_mime_type mime_type
- #
- if instance_method(:#{symbol}).arity == 0 # if instance_method(:mime_type).arity == 0
- def #{symbol}(reload = false) # def mime_type(reload = false)
- if reload || !defined?(#{memoized_ivar}) || #{memoized_ivar}.empty? # if reload || !defined?(@_memoized_mime_type) || @_memoized_mime_type.empty?
- #{memoized_ivar} = [#{original_method}] # @_memoized_mime_type = [_unmemoized_mime_type]
- end # end
- #{memoized_ivar}[0] # @_memoized_mime_type[0]
- end # end
- else # else
- def #{symbol}(*args) # def mime_type(*args)
- #{memoized_ivar} ||= {} unless frozen? # @_memoized_mime_type ||= {} unless frozen?
- args_length = method(:#{original_method}).arity # args_length = method(:_unmemoized_mime_type).arity
- if args.length == args_length + 1 && # if args.length == args_length + 1 &&
- (args.last == true || args.last == :reload) # (args.last == true || args.last == :reload)
- reload = args.pop # reload = args.pop
- end # end
- #
- if defined?(#{memoized_ivar}) && #{memoized_ivar} # if defined?(@_memoized_mime_type) && @_memoized_mime_type
- if !reload && #{memoized_ivar}.has_key?(args) # if !reload && @_memoized_mime_type.has_key?(args)
- #{memoized_ivar}[args] # @_memoized_mime_type[args]
- elsif #{memoized_ivar} # elsif @_memoized_mime_type
- #{memoized_ivar}[args] = #{original_method}(*args) # @_memoized_mime_type[args] = _unmemoized_mime_type(*args)
- end # end
- else # else
- #{original_method}(*args) # _unmemoized_mime_type(*args)
- end # end
- end # end
- end # end
- #
- if private_method_defined?(#{original_method.inspect}) # if private_method_defined?(:_unmemoized_mime_type)
- private #{symbol.inspect} # private :mime_type
- elsif protected_method_defined?(#{original_method.inspect}) # elsif protected_method_defined?(:_unmemoized_mime_type)
- protected #{symbol.inspect} # protected :mime_type
- end # end
- EOS
- end
- end
- end
-end
diff --git a/activesupport/lib/active_support/message_encryptor.rb b/activesupport/lib/active_support/message_encryptor.rb
index 4f7cd12d48..ada2e79ccb 100644
--- a/activesupport/lib/active_support/message_encryptor.rb
+++ b/activesupport/lib/active_support/message_encryptor.rb
@@ -1,5 +1,5 @@
require 'openssl'
-require 'active_support/base64'
+require 'base64'
module ActiveSupport
# MessageEncryptor is a simple way to encrypt values which get stored somewhere
@@ -9,16 +9,56 @@ 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)
+ value
+ end
+
+ def self.dump(value)
+ value
+ end
+ end
+
class InvalidMessage < StandardError; end
OpenSSLCipherError = OpenSSL::Cipher.const_defined?(:CipherError) ? OpenSSL::Cipher::CipherError : OpenSSL::CipherError
- def initialize(secret, cipher = 'aes-256-cbc')
+ # 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 = cipher
+ @cipher = options[:cipher] || 'aes-256-cbc'
+ @verifier = MessageVerifier.new(@secret, :serializer => NullSerializer)
+ @serializer = options[:serializer] || Marshal
+ end
+
+ # Encrypt and sign a message. We need to sign the message in order to avoid padding attacks.
+ # Reference: http://www.limited-entropy.com/padding-oracle-attacks
+ def encrypt_and_sign(value)
+ verifier.generate(_encrypt(value))
+ end
+
+ # Decrypt and verify a message. We need to verify the message in order to avoid padding attacks.
+ # Reference: http://www.limited-entropy.com/padding-oracle-attacks
+ def decrypt_and_verify(value)
+ _decrypt(verifier.verify(value))
end
- def encrypt(value)
+ private
+
+ def _encrypt(value)
cipher = new_cipher
# Rely on OpenSSL for the initialization vector
iv = cipher.random_iv
@@ -27,15 +67,15 @@ module ActiveSupport
cipher.key = @secret
cipher.iv = iv
- encrypted_data = cipher.update(Marshal.dump(value))
+ encrypted_data = cipher.update(@serializer.dump(value))
encrypted_data << cipher.final
- [encrypted_data, iv].map {|v| ActiveSupport::Base64.encode64s(v)}.join("--")
+ [encrypted_data, iv].map {|v| ::Base64.strict_encode64(v)}.join("--")
end
- def decrypt(encrypted_message)
+ def _decrypt(encrypted_message)
cipher = new_cipher
- encrypted_data, iv = encrypted_message.split("--").map {|v| ActiveSupport::Base64.decode64(v)}
+ encrypted_data, iv = encrypted_message.split("--").map {|v| ::Base64.decode64(v)}
cipher.decrypt
cipher.key = @secret
@@ -44,28 +84,17 @@ module ActiveSupport
decrypted_data = cipher.update(encrypted_data)
decrypted_data << cipher.final
- Marshal.load(decrypted_data)
+ @serializer.load(decrypted_data)
rescue OpenSSLCipherError, TypeError
raise InvalidMessage
end
- def encrypt_and_sign(value)
- verifier.generate(encrypt(value))
+ def new_cipher
+ OpenSSL::Cipher::Cipher.new(@cipher)
end
- def decrypt_and_verify(value)
- decrypt(verifier.verify(value))
+ def verifier
+ @verifier
end
-
-
-
- private
- def new_cipher
- OpenSSL::Cipher::Cipher.new(@cipher)
- end
-
- def verifier
- MessageVerifier.new(@secret)
- end
end
end
diff --git a/activesupport/lib/active_support/message_verifier.rb b/activesupport/lib/active_support/message_verifier.rb
index 8f3946325a..3b27089fa0 100644
--- a/activesupport/lib/active_support/message_verifier.rb
+++ b/activesupport/lib/active_support/message_verifier.rb
@@ -1,4 +1,4 @@
-require 'active_support/base64'
+require 'base64'
require 'active_support/core_ext/object/blank'
module ActiveSupport
@@ -18,12 +18,18 @@ module ActiveSupport
# self.current_user = User.find(id)
# end
#
+ # By default it uses Marshal to serialize the message. If you want to use another
+ # serialization method, you can set the serializer attribute to something that responds
+ # to dump and load, e.g.:
+ #
+ # @verifier.serializer = YAML
class MessageVerifier
class InvalidSignature < StandardError; end
- def initialize(secret, digest = 'SHA1')
+ def initialize(secret, options = {})
@secret = secret
- @digest = digest
+ @digest = options[:digest] || 'SHA1'
+ @serializer = options[:serializer] || Marshal
end
def verify(signed_message)
@@ -31,14 +37,14 @@ module ActiveSupport
data, digest = signed_message.split("--")
if data.present? && digest.present? && secure_compare(digest, generate_digest(data))
- Marshal.load(ActiveSupport::Base64.decode64(data))
+ @serializer.load(::Base64.decode64(data))
else
raise InvalidSignature
end
end
def generate(value)
- data = ActiveSupport::Base64.encode64s(Marshal.dump(value))
+ data = ::Base64.strict_encode64(@serializer.dump(value))
"#{data}--#{generate_digest(data)}"
end
diff --git a/activesupport/lib/active_support/multibyte.rb b/activesupport/lib/active_support/multibyte.rb
index 57e8e24bf4..5efe13c537 100644
--- a/activesupport/lib/active_support/multibyte.rb
+++ b/activesupport/lib/active_support/multibyte.rb
@@ -1,9 +1,5 @@
-# encoding: utf-8
-require 'active_support/core_ext/module/attribute_accessors'
-
module ActiveSupport #:nodoc:
module Multibyte
- autoload :EncodingError, 'active_support/multibyte/exceptions'
autoload :Chars, 'active_support/multibyte/chars'
autoload :Unicode, 'active_support/multibyte/unicode'
@@ -21,24 +17,5 @@ module ActiveSupport #:nodoc:
def self.proxy_class
@proxy_class ||= ActiveSupport::Multibyte::Chars
end
-
- # Regular expressions that describe valid byte sequences for a character
- VALID_CHARACTER = {
- # Borrowed from the Kconv library by Shinji KONO - (also as seen on the W3C site)
- 'UTF-8' => /\A(?:
- [\x00-\x7f] |
- [\xc2-\xdf] [\x80-\xbf] |
- \xe0 [\xa0-\xbf] [\x80-\xbf] |
- [\xe1-\xef] [\x80-\xbf] [\x80-\xbf] |
- \xf0 [\x90-\xbf] [\x80-\xbf] [\x80-\xbf] |
- [\xf1-\xf3] [\x80-\xbf] [\x80-\xbf] [\x80-\xbf] |
- \xf4 [\x80-\x8f] [\x80-\xbf] [\x80-\xbf])\z /xn,
- # Quick check for valid Shift-JIS characters, disregards the odd-even pairing
- 'Shift_JIS' => /\A(?:
- [\x00-\x7e\xa1-\xdf] |
- [\x81-\x9f\xe0-\xef] [\x40-\x7e\x80-\x9e\x9f-\xfc])\z /xn
- }
end
-end
-
-require 'active_support/multibyte/utils' \ No newline at end of file
+end \ No newline at end of file
diff --git a/activesupport/lib/active_support/multibyte/chars.rb b/activesupport/lib/active_support/multibyte/chars.rb
index b78d92f599..9a748dfa60 100644
--- a/activesupport/lib/active_support/multibyte/chars.rb
+++ b/activesupport/lib/active_support/multibyte/chars.rb
@@ -1,6 +1,8 @@
# 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'
module ActiveSupport #:nodoc:
module Multibyte #:nodoc:
@@ -34,27 +36,24 @@ module ActiveSupport #:nodoc:
#
# ActiveSupport::Multibyte.proxy_class = CharsForUTF32
class Chars
+ include Comparable
attr_reader :wrapped_string
alias to_s wrapped_string
alias to_str wrapped_string
- if RUBY_VERSION >= "1.9"
- # Creates a new Chars instance by wrapping _string_.
- def initialize(string)
- @wrapped_string = string
- @wrapped_string.force_encoding(Encoding::UTF_8) unless @wrapped_string.frozen?
- end
- else
- def initialize(string) #:nodoc:
- @wrapped_string = string
- end
+ delegate :<=>, :=~, :acts_like_string?, :to => :wrapped_string
+
+ # Creates a new Chars instance by wrapping _string_.
+ def initialize(string)
+ @wrapped_string = string
+ @wrapped_string.force_encoding(Encoding::UTF_8) unless @wrapped_string.frozen?
end
# Forward all undefined methods to the wrapped string.
def method_missing(method, *args, &block)
if method.to_s =~ /!$/
- @wrapped_string.__send__(method, *args, &block)
- self
+ result = @wrapped_string.__send__(method, *args, &block)
+ self if result
else
result = @wrapped_string.__send__(method, *args, &block)
result.kind_of?(String) ? chars(result) : result
@@ -67,178 +66,9 @@ module ActiveSupport #:nodoc:
super || @wrapped_string.respond_to?(method, include_private)
end
- # Enable more predictable duck-typing on String-like classes. See Object#acts_like?.
- def acts_like_string?
- true
- end
-
# Returns +true+ when the proxy class can handle the string. Returns +false+ otherwise.
def self.consumes?(string)
- # Unpack is a little bit faster than regular expressions.
- string.unpack('U*')
- true
- rescue ArgumentError
- false
- end
-
- include Comparable
-
- # Returns -1, 0, or 1, depending on whether the Chars object is to be sorted before,
- # equal or after the object on the right side of the operation. It accepts any object
- # that implements +to_s+:
- #
- # 'é'.mb_chars <=> 'ü'.mb_chars # => -1
- #
- # See <tt>String#<=></tt> for more details.
- def <=>(other)
- @wrapped_string <=> other.to_s
- end
-
- if RUBY_VERSION < "1.9"
- # Returns +true+ if the Chars class can and should act as a proxy for the string _string_. Returns
- # +false+ otherwise.
- def self.wants?(string)
- $KCODE == 'UTF8' && consumes?(string)
- end
-
- # Returns a new Chars object containing the _other_ object concatenated to the string.
- #
- # Example:
- # ('Café'.mb_chars + ' périferôl').to_s # => "Café périferôl"
- def +(other)
- chars(@wrapped_string + other)
- end
-
- # Like <tt>String#=~</tt> only it returns the character offset (in codepoints) instead of the byte offset.
- #
- # Example:
- # 'Café périferôl'.mb_chars =~ /ô/ # => 12
- def =~(other)
- translate_offset(@wrapped_string =~ other)
- end
-
- # Inserts the passed string at specified codepoint offsets.
- #
- # Example:
- # 'Café'.mb_chars.insert(4, ' périferôl').to_s # => "Café périferôl"
- def insert(offset, fragment)
- unpacked = Unicode.u_unpack(@wrapped_string)
- unless offset > unpacked.length
- @wrapped_string.replace(
- Unicode.u_unpack(@wrapped_string).insert(offset, *Unicode.u_unpack(fragment)).pack('U*')
- )
- else
- raise IndexError, "index #{offset} out of string"
- end
- self
- end
-
- # Returns +true+ if contained string contains _other_. Returns +false+ otherwise.
- #
- # Example:
- # 'Café'.mb_chars.include?('é') # => true
- def include?(other)
- # We have to redefine this method because Enumerable defines it.
- @wrapped_string.include?(other)
- end
-
- # Returns the position _needle_ in the string, counting in codepoints. Returns +nil+ if _needle_ isn't found.
- #
- # Example:
- # 'Café périferôl'.mb_chars.index('ô') # => 12
- # 'Café périferôl'.mb_chars.index(/\w/u) # => 0
- def index(needle, offset=0)
- wrapped_offset = first(offset).wrapped_string.length
- index = @wrapped_string.index(needle, wrapped_offset)
- index ? (Unicode.u_unpack(@wrapped_string.slice(0...index)).size) : nil
- end
-
- # Returns the position _needle_ in the string, counting in
- # codepoints, searching backward from _offset_ or the end of the
- # string. Returns +nil+ if _needle_ isn't found.
- #
- # Example:
- # 'Café périferôl'.mb_chars.rindex('é') # => 6
- # 'Café périferôl'.mb_chars.rindex(/\w/u) # => 13
- def rindex(needle, offset=nil)
- offset ||= length
- wrapped_offset = first(offset).wrapped_string.length
- index = @wrapped_string.rindex(needle, wrapped_offset)
- index ? (Unicode.u_unpack(@wrapped_string.slice(0...index)).size) : nil
- end
-
- # Returns the number of codepoints in the string
- def size
- Unicode.u_unpack(@wrapped_string).size
- end
- alias_method :length, :size
-
- # Strips entire range of Unicode whitespace from the right of the string.
- def rstrip
- chars(@wrapped_string.gsub(Unicode::TRAILERS_PAT, ''))
- end
-
- # Strips entire range of Unicode whitespace from the left of the string.
- def lstrip
- chars(@wrapped_string.gsub(Unicode::LEADERS_PAT, ''))
- end
-
- # Strips entire range of Unicode whitespace from the right and left of the string.
- def strip
- rstrip.lstrip
- end
-
- # Returns the codepoint of the first character in the string.
- #
- # Example:
- # 'こんにちは'.mb_chars.ord # => 12371
- def ord
- Unicode.u_unpack(@wrapped_string)[0]
- end
-
- # Works just like <tt>String#rjust</tt>, only integer specifies characters instead of bytes.
- #
- # Example:
- #
- # "¾ cup".mb_chars.rjust(8).to_s
- # # => " ¾ cup"
- #
- # "¾ cup".mb_chars.rjust(8, " ").to_s # Use non-breaking whitespace
- # # => "   ¾ cup"
- def rjust(integer, padstr=' ')
- justify(integer, :right, padstr)
- end
-
- # Works just like <tt>String#ljust</tt>, only integer specifies characters instead of bytes.
- #
- # Example:
- #
- # "¾ cup".mb_chars.rjust(8).to_s
- # # => "¾ cup "
- #
- # "¾ cup".mb_chars.rjust(8, " ").to_s # Use non-breaking whitespace
- # # => "¾ cup   "
- def ljust(integer, padstr=' ')
- justify(integer, :left, padstr)
- end
-
- # Works just like <tt>String#center</tt>, only integer specifies characters instead of bytes.
- #
- # Example:
- #
- # "¾ cup".mb_chars.center(8).to_s
- # # => " ¾ cup "
- #
- # "¾ cup".mb_chars.center(8, " ").to_s # Use non-breaking whitespace
- # # => " ¾ cup  "
- def center(integer, padstr=' ')
- justify(integer, :center, padstr)
- end
-
- else
- def =~(other)
- @wrapped_string =~ other
- end
+ string.encoding == Encoding::UTF_8
end
# Works just like <tt>String#split</tt>, with the exception that the items in the resulting list are Chars
@@ -250,45 +80,10 @@ module ActiveSupport #:nodoc:
@wrapped_string.split(*args).map { |i| i.mb_chars }
end
- # Like <tt>String#[]=</tt>, except instead of byte offsets you specify character offsets.
- #
- # Example:
- #
- # s = "Müller"
- # s.mb_chars[2] = "e" # Replace character with offset 2
- # s
- # # => "Müeler"
- #
- # s = "Müller"
- # s.mb_chars[1, 2] = "ö" # Replace 2 characters at character offset 1
- # s
- # # => "Möler"
- def []=(*args)
- replace_by = args.pop
- # Indexed replace with regular expressions already works
- if args.first.is_a?(Regexp)
- @wrapped_string[*args] = replace_by
- else
- result = Unicode.u_unpack(@wrapped_string)
- case args.first
- when Fixnum
- raise IndexError, "index #{args[0]} out of string" if args[0] >= result.length
- min = args[0]
- max = args[1].nil? ? min : (min + args[1] - 1)
- range = Range.new(min, max)
- replace_by = [replace_by].pack('U') if replace_by.is_a?(Fixnum)
- when Range
- raise RangeError, "#{args[0]} out of range" if args[0].min >= result.length
- range = args[0]
- else
- needle = args[0].to_s
- min = index(needle)
- max = min + Unicode.u_unpack(needle).length - 1
- range = Range.new(min, max)
- end
- result[range] = Unicode.u_unpack(replace_by)
- @wrapped_string.replace(result.pack('U*'))
- end
+ # Works like like <tt>String#slice!</tt>, but returns an instance of Chars, or nil if the string was not
+ # modified.
+ def slice!(*args)
+ chars(@wrapped_string.slice!(*args))
end
# Reverses all characters in the string.
@@ -296,38 +91,10 @@ module ActiveSupport #:nodoc:
# Example:
# 'Café'.mb_chars.reverse.to_s # => 'éfaC'
def reverse
- chars(Unicode.g_unpack(@wrapped_string).reverse.flatten.pack('U*'))
+ chars(Unicode.unpack_graphemes(@wrapped_string).reverse.flatten.pack('U*'))
end
- # Implements Unicode-aware slice with codepoints. Slicing on one point returns the codepoints for that
- # character.
- #
- # Example:
- # 'こんにちは'.mb_chars.slice(2..3).to_s # => "にち"
- def slice(*args)
- if args.size > 2
- raise ArgumentError, "wrong number of arguments (#{args.size} for 1)" # Do as if we were native
- elsif (args.size == 2 && !(args.first.is_a?(Numeric) || args.first.is_a?(Regexp)))
- raise TypeError, "cannot convert #{args.first.class} into Integer" # Do as if we were native
- elsif (args.size == 2 && !args[1].is_a?(Numeric))
- raise TypeError, "cannot convert #{args[1].class} into Integer" # Do as if we were native
- elsif args[0].kind_of? Range
- cps = Unicode.u_unpack(@wrapped_string).slice(*args)
- result = cps.nil? ? nil : cps.pack('U*')
- elsif args[0].kind_of? Regexp
- result = @wrapped_string.slice(*args)
- elsif args.size == 1 && args[0].kind_of?(Numeric)
- character = Unicode.u_unpack(@wrapped_string)[args[0]]
- result = character && [character].pack('U')
- else
- cps = Unicode.u_unpack(@wrapped_string).slice(*args)
- result = cps && cps.pack('U*')
- end
- result && chars(result)
- end
- alias_method :[], :slice
-
- # 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:
@@ -336,20 +103,28 @@ 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 ?"
def upcase
- chars(Unicode.apply_mapping @wrapped_string, :uppercase_mapping)
+ 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"
def downcase
- chars(Unicode.apply_mapping @wrapped_string, :lowercase_mapping)
+ 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.
@@ -366,7 +141,7 @@ module ActiveSupport #:nodoc:
# "ÉL QUE SE ENTERÓ".mb_chars.titleize # => "Él Que Se Enteró"
# "日本語".mb_chars.titleize # => "日本語"
def titleize
- chars(downcase.to_s.gsub(/\b('?[\S])/u) { Unicode.apply_mapping $1, :uppercase_mapping })
+ chars(downcase.to_s.gsub(/\b('?[\S])/u) { Unicode.upcase($1)})
end
alias_method :titlecase, :titleize
@@ -386,7 +161,7 @@ module ActiveSupport #:nodoc:
# 'é'.length # => 2
# 'é'.mb_chars.decompose.to_s.length # => 3
def decompose
- chars(Unicode.decompose_codepoints(:canonical, Unicode.u_unpack(@wrapped_string)).pack('U*'))
+ chars(Unicode.decompose(:canonical, @wrapped_string.codepoints.to_a).pack('U*'))
end
# Performs composition on all the characters.
@@ -395,16 +170,16 @@ module ActiveSupport #:nodoc:
# 'é'.length # => 3
# 'é'.mb_chars.compose.to_s.length # => 2
def compose
- chars(Unicode.compose_codepoints(Unicode.u_unpack(@wrapped_string)).pack('U*'))
+ chars(Unicode.compose(@wrapped_string.codepoints.to_a).pack('U*'))
end
# Returns the number of grapheme clusters in the string.
#
# Example:
# 'क्षि'.mb_chars.length # => 4
- # 'क्षि'.mb_chars.g_length # => 3
- def g_length
- Unicode.g_unpack(@wrapped_string).length
+ # 'क्षि'.mb_chars.grapheme_length # => 3
+ def grapheme_length
+ Unicode.unpack_graphemes(@wrapped_string).length
end
# Replaces all ISO-8859-1 or CP1252 characters by their UTF-8 equivalent resulting in a valid UTF-8 string.
@@ -414,14 +189,14 @@ module ActiveSupport #:nodoc:
chars(Unicode.tidy_bytes(@wrapped_string, force))
end
- %w(capitalize downcase lstrip reverse rstrip slice strip tidy_bytes upcase).each do |method|
- # Only define a corresponding bang method for methods defined in the proxy; On 1.9 the proxy will
- # exclude lstrip!, rstrip! and strip! because they are already work as expected on multibyte strings.
- if public_method_defined?(method)
- define_method("#{method}!") do |*args|
- @wrapped_string = send(args.nil? ? method : method, *args).to_s
- self
- 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
+ self
end
end
@@ -431,43 +206,14 @@ module ActiveSupport #:nodoc:
return nil if byte_offset.nil?
return 0 if @wrapped_string == ''
- if @wrapped_string.respond_to?(:force_encoding)
- @wrapped_string = @wrapped_string.dup.force_encoding(Encoding::ASCII_8BIT)
- end
-
begin
- @wrapped_string[0...byte_offset].unpack('U*').length
+ @wrapped_string.byteslice(0...byte_offset).unpack('U*').length
rescue ArgumentError
byte_offset -= 1
retry
end
end
- def justify(integer, way, padstr=' ') #:nodoc:
- raise ArgumentError, "zero width padding" if padstr.length == 0
- padsize = integer - size
- padsize = padsize > 0 ? padsize : 0
- case way
- when :right
- result = @wrapped_string.dup.insert(0, padding(padsize, padstr))
- when :left
- result = @wrapped_string.dup.insert(-1, padding(padsize, padstr))
- when :center
- lpad = padding((padsize / 2.0).floor, padstr)
- rpad = padding((padsize / 2.0).ceil, padstr)
- result = @wrapped_string.dup.insert(0, lpad).insert(-1, rpad)
- end
- chars(result)
- end
-
- def padding(padsize, padstr=' ') #:nodoc:
- if padsize != 0
- chars(padstr * ((padsize / Unicode.u_unpack(padstr).size) + 1)).slice(0, padsize)
- else
- ''
- end
- end
-
def chars(string) #:nodoc:
self.class.new(string)
end
diff --git a/activesupport/lib/active_support/multibyte/exceptions.rb b/activesupport/lib/active_support/multibyte/exceptions.rb
deleted file mode 100644
index 62066e3c71..0000000000
--- a/activesupport/lib/active_support/multibyte/exceptions.rb
+++ /dev/null
@@ -1,8 +0,0 @@
-# encoding: utf-8
-
-module ActiveSupport #:nodoc:
- module Multibyte #:nodoc:
- # Raised when a problem with the encoding was found.
- class EncodingError < StandardError; end
- end
-end \ No newline at end of file
diff --git a/activesupport/lib/active_support/multibyte/unicode.rb b/activesupport/lib/active_support/multibyte/unicode.rb
index 754ca9290b..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 = '5.2.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.
@@ -61,19 +61,6 @@ module ActiveSupport
TRAILERS_PAT = /(#{codepoints_to_pattern(LEADERS_AND_TRAILERS)})+\Z/u
LEADERS_PAT = /\A(#{codepoints_to_pattern(LEADERS_AND_TRAILERS)})+/u
- # Unpack the string at codepoints boundaries. Raises an EncodingError when the encoding of the string isn't
- # valid UTF-8.
- #
- # Example:
- # Unicode.u_unpack('Café') # => [67, 97, 102, 233]
- def u_unpack(string)
- begin
- string.unpack 'U*'
- rescue ArgumentError
- raise EncodingError, 'malformed UTF-8 character'
- end
- end
-
# Detect whether the codepoint is in a certain character class. Returns +true+ when it's in the specified
# character class and +false+ otherwise. Valid character classes are: <tt>:cr</tt>, <tt>:lf</tt>, <tt>:l</tt>,
# <tt>:v</tt>, <tt>:lv</tt>, <tt>:lvt</tt> and <tt>:t</tt>.
@@ -86,10 +73,10 @@ module ActiveSupport
# Unpack the string at grapheme boundaries. Returns a list of character lists.
#
# Example:
- # Unicode.g_unpack('क्षि') # => [[2325, 2381], [2359], [2367]]
- # Unicode.g_unpack('Café') # => [[67], [97], [102], [233]]
- def g_unpack(string)
- codepoints = u_unpack(string)
+ # Unicode.unpack_graphemes('क्षि') # => [[2325, 2381], [2359], [2367]]
+ # Unicode.unpack_graphemes('Café') # => [[67], [97], [102], [233]]
+ def unpack_graphemes(string)
+ codepoints = string.codepoints.to_a
unpacked = []
pos = 0
marker = 0
@@ -118,12 +105,12 @@ module ActiveSupport
unpacked
end
- # Reverse operation of g_unpack.
+ # Reverse operation of unpack_graphemes.
#
# Example:
- # Unicode.g_pack(Unicode.g_unpack('क्षि')) # => 'क्षि'
- def g_pack(unpacked)
- (unpacked.flatten).pack('U*')
+ # Unicode.pack_graphemes(Unicode.unpack_graphemes('क्षि')) # => 'क्षि'
+ def pack_graphemes(unpacked)
+ unpacked.flatten.pack('U*')
end
# Re-order codepoints so the string becomes canonical.
@@ -143,7 +130,7 @@ module ActiveSupport
end
# Decompose composed characters to the decomposed form.
- def decompose_codepoints(type, codepoints)
+ def decompose(type, codepoints)
codepoints.inject([]) do |decomposed, cp|
# if it's a hangul syllable starter character
if HANGUL_SBASE <= cp and cp < HANGUL_SLAST
@@ -156,7 +143,7 @@ module ActiveSupport
decomposed.concat ncp
# if the codepoint is decomposable in with the current decomposition type
elsif (ncp = database.codepoints[cp].decomp_mapping) and (!database.codepoints[cp].decomp_type || type == :compatability)
- decomposed.concat decompose_codepoints(type, ncp.dup)
+ decomposed.concat decompose(type, ncp.dup)
else
decomposed << cp
end
@@ -164,7 +151,7 @@ module ActiveSupport
end
# Compose decomposed characters to the composed form.
- def compose_codepoints(codepoints)
+ def compose(codepoints)
pos = 0
eoa = codepoints.length - 1
starter_pos = 0
@@ -283,35 +270,40 @@ module ActiveSupport
def normalize(string, form=nil)
form ||= @default_normalization_form
# See http://www.unicode.org/reports/tr15, Table 1
- codepoints = u_unpack(string)
+ codepoints = string.codepoints.to_a
case form
when :d
- reorder_characters(decompose_codepoints(:canonical, codepoints))
+ reorder_characters(decompose(:canonical, codepoints))
when :c
- compose_codepoints(reorder_characters(decompose_codepoints(:canonical, codepoints)))
+ compose(reorder_characters(decompose(:canonical, codepoints)))
when :kd
- reorder_characters(decompose_codepoints(:compatability, codepoints))
+ reorder_characters(decompose(:compatability, codepoints))
when :kc
- compose_codepoints(reorder_characters(decompose_codepoints(:compatability, codepoints)))
+ compose(reorder_characters(decompose(:compatability, codepoints)))
else
raise ArgumentError, "#{form} is not a valid normalization variant", caller
end.pack('U*')
end
- def apply_mapping(string, mapping) #:nodoc:
- u_unpack(string).map do |codepoint|
- cp = database.codepoints[codepoint]
- if cp and (ncp = cp.send(mapping)) and ncp > 0
- ncp
- else
- codepoint
- end
- end.pack('U*')
+ def downcase(string)
+ apply_mapping string, :lowercase_mapping
+ end
+
+ def upcase(string)
+ 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
@@ -374,6 +366,17 @@ module ActiveSupport
private
+ def apply_mapping(string, mapping) #:nodoc:
+ string.each_codepoint.map do |codepoint|
+ cp = database.codepoints[codepoint]
+ if cp and (ncp = cp.send(mapping)) and ncp > 0
+ ncp
+ else
+ codepoint
+ end
+ end.pack('U*')
+ end
+
def tidy_byte(byte)
if byte < 160
[database.cp1252[byte] || byte].pack("U").unpack("C*")
diff --git a/activesupport/lib/active_support/multibyte/utils.rb b/activesupport/lib/active_support/multibyte/utils.rb
deleted file mode 100644
index 94b393cee2..0000000000
--- a/activesupport/lib/active_support/multibyte/utils.rb
+++ /dev/null
@@ -1,60 +0,0 @@
-# encoding: utf-8
-
-module ActiveSupport #:nodoc:
- module Multibyte #:nodoc:
- if Kernel.const_defined?(:Encoding)
- # Returns a regular expression that matches valid characters in the current encoding
- def self.valid_character
- VALID_CHARACTER[Encoding.default_external.to_s]
- end
- else
- def self.valid_character
- case $KCODE
- when 'UTF8'
- VALID_CHARACTER['UTF-8']
- when 'SJIS'
- VALID_CHARACTER['Shift_JIS']
- end
- end
- end
-
- if 'string'.respond_to?(:valid_encoding?)
- # Verifies the encoding of a string
- def self.verify(string)
- string.valid_encoding?
- end
- else
- def self.verify(string)
- if expression = valid_character
- # Splits the string on character boundaries, which are determined based on $KCODE.
- string.split(//).all? { |c| expression =~ c }
- else
- true
- end
- end
- end
-
- # Verifies the encoding of the string and raises an exception when it's not valid
- def self.verify!(string)
- raise EncodingError.new("Found characters with invalid encoding") unless verify(string)
- end
-
- if 'string'.respond_to?(:force_encoding)
- # Removes all invalid characters from the string.
- #
- # Note: this method is a no-op in Ruby 1.9
- def self.clean(string)
- string
- end
- else
- def self.clean(string)
- if expression = valid_character
- # Splits the string on character boundaries, which are determined based on $KCODE.
- string.split(//).grep(expression).join
- else
- string
- end
- end
- end
- end
-end
diff --git a/activesupport/lib/active_support/notifications.rb b/activesupport/lib/active_support/notifications.rb
index 77696eb1db..6735c561d3 100644
--- a/activesupport/lib/active_support/notifications.rb
+++ b/activesupport/lib/active_support/notifications.rb
@@ -1,46 +1,140 @@
-require 'active_support/core_ext/module/delegation'
+require 'active_support/notifications/instrumenter'
+require 'active_support/notifications/fanout'
module ActiveSupport
- # Notifications provides an instrumentation API for Ruby. To instrument an
- # action in Ruby you just need to do:
+ # = Notifications
#
- # ActiveSupport::Notifications.instrument(:render, :extra => :information) do
+ # <tt>ActiveSupport::Notifications</tt> provides an instrumentation API for Ruby.
+ #
+ # == Instrumenters
+ #
+ # To instrument an event you just need to do:
+ #
+ # ActiveSupport::Notifications.instrument("render", :extra => :information) do
# render :text => "Foo"
# end
#
+ # That executes the block first and notifies all subscribers once done.
+ #
+ # In the example above "render" is the name of the event, and the rest is called
+ # the _payload_. The payload is a mechanism that allows instrumenters to pass
+ # extra information to subscribers. Payloads consist of a hash whose contents
+ # are arbitrary and generally depend on the event.
+ #
+ # == Subscribers
+ #
# You can consume those events and the information they provide by registering
- # a log subscriber. For instance, let's store all instrumented events in an array:
+ # a subscriber. For instance, let's store all "render" events in an array:
#
- # @events = []
+ # events = []
#
- # ActiveSupport::Notifications.subscribe do |*args|
- # @events << ActiveSupport::Notifications::Event.new(*args)
+ # ActiveSupport::Notifications.subscribe("render") do |*args|
+ # events << ActiveSupport::Notifications::Event.new(*args)
# end
#
- # ActiveSupport::Notifications.instrument(:render, :extra => :information) do
+ # That code returns right away, you are just subscribing to "render" events.
+ # The block will be called asynchronously whenever someone instruments "render":
+ #
+ # ActiveSupport::Notifications.instrument("render", :extra => :information) do
# render :text => "Foo"
# end
#
- # event = @events.first
- # event.name # => :render
+ # event = events.first
+ # event.name # => "render"
# event.duration # => 10 (in milliseconds)
# event.payload # => { :extra => :information }
#
- # When subscribing to Notifications, you can pass a pattern, to only consume
- # events that match the pattern:
+ # 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 <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 <tt>ActiveSupport::Notifications::Event</tt>
+ # is able to take the arguments as they come and provide an object-oriented
+ # interface to that data.
#
- # ActiveSupport::Notifications.subscribe(/render/) do |event|
- # @render_events << event
+ # 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 <tt>subscribe</tt>, in which case you are subscribing
+ # to all events.
+ #
+ # == Temporary Subscriptions
+ #
+ # 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
+ # example, in
+ #
+ # callback = lambda {|*args| ... }
+ # ActiveSupport::Notifications.subscribed(callback, "sql.active_record") do
+ # ...
+ # end
+ #
+ # the callback will be called for all "sql.active_record" events instrumented
+ # during the execution of the block. The callback is unsubscribed automatically
+ # after that.
+ #
+ # === Manual Unsubscription
+ #
+ # The +subscribe+ method returns a subscriber object:
+ #
+ # subscriber = ActiveSupport::Notifications.subscribe("render") do |*args|
+ # ...
+ # end
+ #
+ # To prevent that block from being called anymore, just unsubscribe passing
+ # that reference:
+ #
+ # ActiveSupport::Notifications.unsubscribe(subscriber)
+ #
+ # == Default Queue
+ #
# Notifications ships with a queue implementation that consumes and publish events
# 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
@@ -64,6 +158,13 @@ module ActiveSupport
end
end
+ def subscribed(callback, *args, &block)
+ subscriber = subscribe(*args, &callback)
+ yield
+ ensure
+ unsubscribe(subscriber)
+ end
+
def unsubscribe(args)
notifier.unsubscribe(args)
@instrumenters.clear
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_hash.rb b/activesupport/lib/active_support/ordered_hash.rb
index 7f70628933..8edd3960c7 100644
--- a/activesupport/lib/active_support/ordered_hash.rb
+++ b/activesupport/lib/active_support/ordered_hash.rb
@@ -1,8 +1,3 @@
-begin
- require 'psych'
-rescue LoadError
-end
-
require 'yaml'
YAML.add_builtin_type("omap") do |type, val|
@@ -20,7 +15,7 @@ module ActiveSupport
# oh.keys # => [:a, :b], this order is guaranteed
#
# <tt>ActiveSupport::OrderedHash</tt> is namespaced to prevent conflicts with other implementations.
- class OrderedHash < ::Hash #:nodoc:
+ class OrderedHash < ::Hash
def to_yaml_type
"!tag:yaml.org,2002:omap"
end
@@ -29,189 +24,13 @@ module ActiveSupport
coder.represent_seq '!omap', map { |k,v| { k => v } }
end
- def to_yaml(opts = {})
- if YAML.const_defined?(:ENGINE) && !YAML::ENGINE.syck?
- return super
- end
-
- YAML.quick_emit(self, opts) do |out|
- out.seq(taguri) do |seq|
- each do |k, v|
- seq.add(k => v)
- end
- end
- end
- end
-
def nested_under_indifferent_access
self
end
- # Hash is ordered in Ruby 1.9!
- if RUBY_VERSION < '1.9'
-
- # In MRI the Hash class is core and written in C. In particular, methods are
- # programmed with explicit C function calls and polymorphism is not honored.
- #
- # For example, []= is crucial in this implementation to maintain the @keys
- # array but hash.c invokes rb_hash_aset() originally. This prevents method
- # reuse through inheritance and forces us to reimplement stuff.
- #
- # For instance, we cannot use the inherited #merge! because albeit the algorithm
- # itself would work, our []= is not being called at all by the C code.
-
- def initialize(*args, &block)
- super
- @keys = []
- end
-
- def self.[](*args)
- ordered_hash = new
-
- if (args.length == 1 && args.first.is_a?(Array))
- args.first.each do |key_value_pair|
- next unless (key_value_pair.is_a?(Array))
- ordered_hash[key_value_pair[0]] = key_value_pair[1]
- end
-
- return ordered_hash
- end
-
- unless (args.size % 2 == 0)
- raise ArgumentError.new("odd number of arguments for Hash")
- end
-
- args.each_with_index do |val, ind|
- next if (ind % 2 != 0)
- ordered_hash[val] = args[ind + 1]
- end
-
- ordered_hash
- end
-
- def initialize_copy(other)
- super
- # make a deep copy of keys
- @keys = other.keys
- end
-
- def []=(key, value)
- @keys << key unless has_key?(key)
- super
- end
-
- def delete(key)
- if has_key? key
- index = @keys.index(key)
- @keys.delete_at index
- end
- super
- end
-
- def delete_if
- super
- sync_keys!
- self
- end
-
- def reject!
- super
- sync_keys!
- self
- end
-
- def reject(&block)
- dup.reject!(&block)
- end
-
- def keys
- @keys.dup
- end
-
- def values
- @keys.collect { |key| self[key] }
- end
-
- def to_hash
- self
- end
-
- def to_a
- @keys.map { |key| [ key, self[key] ] }
- end
-
- def each_key
- return to_enum(:each_key) unless block_given?
- @keys.each { |key| yield key }
- self
- end
-
- def each_value
- return to_enum(:each_value) unless block_given?
- @keys.each { |key| yield self[key]}
- self
- end
-
- def each
- return to_enum(:each) unless block_given?
- @keys.each {|key| yield [key, self[key]]}
- self
- end
-
- def each_pair
- return to_enum(:each_pair) unless block_given?
- @keys.each {|key| yield key, self[key]}
- self
- end
-
- alias_method :select, :find_all
-
- def clear
- super
- @keys.clear
- self
- end
-
- def shift
- k = @keys.first
- v = delete(k)
- [k, v]
- end
-
- def merge!(other_hash)
- if block_given?
- other_hash.each { |k, v| self[k] = key?(k) ? yield(k, self[k], v) : v }
- else
- other_hash.each { |k, v| self[k] = v }
- end
- self
- end
-
- alias_method :update, :merge!
-
- def merge(other_hash, &block)
- dup.merge!(other_hash, &block)
- end
-
- # When replacing with another hash, the initial order of our keys must come from the other hash -ordered or not.
- def replace(other)
- super
- @keys = other.keys
- self
- end
-
- def invert
- OrderedHash[self.to_a.map!{|key_value_pair| key_value_pair.reverse}]
- end
-
- def inspect
- "#<OrderedHash #{super}>"
- end
-
- private
- def sync_keys!
- @keys.delete_if {|k| !has_key?(k)}
- end
+ # Returns true to make sure that this hash is extractable via <tt>Array#extract_options!</tt>
+ def extractable_options?
+ true
end
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 04df2ea562..d1c62c5087 100644
--- a/activesupport/lib/active_support/railtie.rb
+++ b/activesupport/lib/active_support/railtie.rb
@@ -1,44 +1,13 @@
require "active_support"
-require "rails"
require "active_support/i18n_railtie"
module ActiveSupport
class Railtie < Rails::Railtie
config.active_support = ActiveSupport::OrderedOptions.new
- # Loads support for "whiny nil" (noisy warnings when methods are invoked
- # on +nil+ values) if Configuration#whiny_nils is true.
- initializer "active_support.initialize_whiny_nils" do |app|
- require 'active_support/whiny_nil' if app.config.whiny_nils
- end
-
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
@@ -49,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..85e84bc203 100644
--- a/activesupport/lib/active_support/rescuable.rb
+++ b/activesupport/lib/active_support/rescuable.rb
@@ -108,7 +108,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 608b3fe4b9..13e96b3596 100644
--- a/activesupport/lib/active_support/ruby/shim.rb
+++ b/activesupport/lib/active_support/ruby/shim.rb
@@ -3,20 +3,13 @@
#
# Date next_year, next_month
# DateTime to_date, to_datetime, xmlschema
-# Enumerable group_by, each_with_object, none?
-# Process Process.daemon
-# REXML security fix
+# Enumerable group_by, none?
# String ord
# Time to_date, to_time, to_datetime
require 'active_support'
require 'active_support/core_ext/date/calculations'
require 'active_support/core_ext/date_time/conversions'
require 'active_support/core_ext/enumerable'
-require 'active_support/core_ext/process/daemon'
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/rexml'
require 'active_support/core_ext/time/conversions'
-require 'active_support/core_ext/file/path'
-require 'active_support/core_ext/module/method_names' \ No newline at end of file
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
new file mode 100644
index 0000000000..538a36f6d9
--- /dev/null
+++ b/activesupport/lib/active_support/tagged_logging.rb
@@ -0,0 +1,58 @@
+require 'active_support/core_ext/object/blank'
+require 'logger'
+require 'active_support/logger'
+
+module ActiveSupport
+ # 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"
+ #
+ # 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.
+ 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 clear!
+ current_tags.clear
+ end
+
+ def current_tags
+ Thread.current[:activesupport_tagged_logging_tags] ||= []
+ end
+
+ private
+ def tags_text
+ tags = current_tags
+ if tags.any?
+ tags.collect { |tag| "[#{tag}] " }.join
+ end
+ end
+ end
+
+ def self.new(logger)
+ logger.formatter.extend Formatter
+ logger.extend(self)
+ 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 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 8d6c27e381..9a52c916ec 100644
--- a/activesupport/lib/active_support/test_case.rb
+++ b/activesupport/lib/active_support/test_case.rb
@@ -1,34 +1,55 @@
-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
- if defined? MiniTest
- Assertion = MiniTest::Assertion
- alias_method :method_name, :name if method_defined? :name
- alias_method :method_name, :__name__ if method_defined? :__name__
- else
- Assertion = Test::Unit::AssertionFailedError
-
- undef :default_test
+ 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__
+
$tags = {}
def self.for_tag(tag)
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/assertions.rb b/activesupport/lib/active_support/testing/assertions.rb
index f3629ada5b..d84595fa8f 100644
--- a/activesupport/lib/active_support/testing/assertions.rb
+++ b/activesupport/lib/active_support/testing/assertions.rb
@@ -1,4 +1,3 @@
-require 'active_support/core_ext/array/wrap'
require 'active_support/core_ext/object/blank'
module ActiveSupport
@@ -45,7 +44,7 @@ module ActiveSupport
# post :delete, :id => ...
# end
def assert_difference(expression, difference = 1, message = nil, &block)
- expressions = Array.wrap expression
+ expressions = Array(expression)
exps = expressions.map { |e|
e.respond_to?(:call) ? e : lambda { eval(e, block.binding) }
@@ -87,7 +86,7 @@ module ActiveSupport
# Test if an expression is not blank. Passes if object.present? is true.
#
- # assert_present {:data => 'x' } # => true
+ # assert_present({:data => 'x' }) # => true
def assert_present(object, message=nil)
message ||= "#{object.inspect} is blank"
assert object.present?, message
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 6b29ba4c10..1a0681e850 100644
--- a/activesupport/lib/active_support/testing/isolation.rb
+++ b/activesupport/lib/active_support/testing/isolation.rb
@@ -37,14 +37,6 @@ module ActiveSupport
!ENV["NO_FORK"] && ((RbConfig::CONFIG['host_os'] !~ /mswin|mingw/) && (RUBY_PLATFORM !~ /java/))
end
- def self.included(base)
- if defined?(::MiniTest) && base < ::MiniTest::Unit::TestCase
- base.send :include, MiniTest
- elsif defined?(Test::Unit)
- base.send :include, TestUnit
- end
- 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)
@@ -52,42 +44,16 @@ module ActiveSupport
end
end
- module TestUnit
- def run(result)
- _run_class_setup
-
- yield(Test::Unit::TestCase::STARTED, name)
-
- @_result = result
-
- serialized = run_in_isolation do |proxy|
- begin
- super(proxy) { }
- rescue Exception => e
- proxy.add_error(Test::Unit::Error.new(name, e))
- end
- end
-
- retval, proxy = Marshal.load(serialized)
- proxy.__replay__(@_result)
+ def run(runner)
+ _run_class_setup
- yield(Test::Unit::TestCase::FINISHED, name)
- retval
+ serialized = run_in_isolation do |isolated_runner|
+ super(isolated_runner)
end
- end
-
- module MiniTest
- 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
- end
+ retval, proxy = Marshal.load(serialized)
+ proxy.__replay__(runner)
+ retval
end
module Forking
@@ -145,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 feac7bc347..0000000000
--- a/activesupport/lib/active_support/testing/pending.rb
+++ /dev/null
@@ -1,52 +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)
- if defined?(::MiniTest)
- skip(description.blank? ? nil : description)
- else
- if description.is_a?(Symbol)
- is_pending = $tags[description]
- return block.call unless is_pending
- end
-
- if block_given?
- failed = false
-
- begin
- block.call
- rescue Exception
- failed = true
- end
-
- flunk("<#{description}> did not fail.") unless failed
- end
-
- caller[0] =~ (/(.*):(.*):in `(.*)'/)
- @@pending_cases << "#{$3} at #{$1}, line #{$2}"
- print "P"
-
- @@at_exit ||= begin
- at_exit do
- puts "\nPending Cases:"
- @@pending_cases.each do |test_case|
- puts test_case
- end
- end
- end
- end
- end
- end
-
- end
- end
-end
diff --git a/activesupport/lib/active_support/testing/performance.rb b/activesupport/lib/active_support/testing/performance.rb
index dd23f8d82d..244ee1a224 100644
--- a/activesupport/lib/active_support/testing/performance.rb
+++ b/activesupport/lib/active_support/testing/performance.rb
@@ -13,12 +13,6 @@ module ActiveSupport
included do
superclass_delegating_accessor :profile_options
self.profile_options = {}
-
- if defined?(MiniTest::Assertions) && TestCase < MiniTest::Assertions
- include ForMiniTest
- else
- include ForClassicTestUnit
- end
end
# each implementation should define metrics and freeze the defaults
@@ -41,82 +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 = '.'
- 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, :enumerator => :reverse_each
- rescue Exception => e
- result = @runner.puke(self.class, method_name, e)
- end
- end
- result
- end
+ return
end
- module ForClassicTestUnit
- def run(result)
- return if method_name =~ /^default_test$/
-
- yield(self.class::STARTED, name)
- @_result = result
-
- 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)
- result.add_run
- else
- puts '%20s: unsupported' % metric_name
- end
- end
- end
-
- yield(self.class::FINISHED, name)
- end
-
- def run_test(metric, mode)
+ def run_test(metric, mode)
+ result = '.'
+ begin
run_callbacks :setup
setup
- metric.send(mode) { __send__ @method_name }
- rescue ::Test::Unit::AssertionFailedError => e
- add_failure(e.message, e.backtrace)
- rescue StandardError, ScriptError => e
- add_error(e)
+ metric.send(mode) { __send__ method_name }
+ rescue Exception => e
+ result = @runner.puke(self.class, method_name, e)
ensure
begin
teardown
run_callbacks :teardown, :enumerator => :reverse_each
- rescue ::Test::Unit::AssertionFailedError => e
- add_failure(e.message, e.backtrace)
- rescue StandardError, ScriptError => e
- add_error(e)
+ rescue Exception => e
+ result = @runner.puke(self.class, method_name, e)
end
end
+ result
end
protected
@@ -208,8 +158,7 @@ module ActiveSupport
end
end
- ruby = defined?(RUBY_ENGINE) ? RUBY_ENGINE : 'ruby'
- ruby += "-#{RUBY_VERSION}.#{RUBY_PATCHLEVEL}"
+ ruby = "#{RUBY_ENGINE}-#{RUBY_VERSION}.#{RUBY_PATCHLEVEL}"
@env = [app, rails, ruby, RUBY_PLATFORM] * ','
end
@@ -306,7 +255,6 @@ module ActiveSupport
end
end
-RUBY_ENGINE = 'ruby' unless defined?(RUBY_ENGINE) # mri 1.8
case RUBY_ENGINE
when 'ruby' then require 'active_support/testing/performance/ruby'
when 'rbx' then require 'active_support/testing/performance/rubinius'
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 7d6d047ef6..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,29 +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'
-elsif RUBY_VERSION.between?('1.8.6', '1.9')
- require 'active_support/testing/performance/ruby/mri'
-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/mri.rb b/activesupport/lib/active_support/testing/performance/ruby/mri.rb
deleted file mode 100644
index 142279dd6e..0000000000
--- a/activesupport/lib/active_support/testing/performance/ruby/mri.rb
+++ /dev/null
@@ -1,57 +0,0 @@
-module ActiveSupport
- module Testing
- module Performance
- module Metrics
- class Base
- protected
- # Ruby 1.8 + ruby-prof wrapper (enable/disable stats for Benchmarker)
- if GC.respond_to?(:enable_stats)
- def with_gc_stats
- GC.enable_stats
- GC.start
- yield
- ensure
- GC.disable_stats
- end
- end
- end
-
- class Memory < DigitalInformationUnit
- # Ruby 1.8 + ruby-prof wrapper
- if RubyProf.respond_to?(:measure_memory)
- def measure
- RubyProf.measure_memory
- end
- end
- end
-
- class Objects < Amount
- # Ruby 1.8 + ruby-prof wrapper
- if RubyProf.respond_to?(:measure_allocations)
- def measure
- RubyProf.measure_allocations
- end
- end
- end
-
- class GcRuns < Amount
- # Ruby 1.8 + ruby-prof wrapper
- if RubyProf.respond_to?(:measure_gc_runs)
- def measure
- RubyProf.measure_gc_runs
- end
- end
- end
-
- class GcTime < Time
- # Ruby 1.8 + ruby-prof wrapper
- if RubyProf.respond_to?(:measure_gc_time)
- def measure
- RubyProf.measure_gc_time / 1000.0 / 1000.0
- end
- end
- end
- end
- end
- end
-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 7873262331..0000000000
--- a/activesupport/lib/active_support/testing/performance/ruby/yarv.rb
+++ /dev/null
@@ -1,57 +0,0 @@
-module ActiveSupport
- module Testing
- module Performance
- module Metrics
- class Base
- protected
- # Ruby 1.9 with GC::Profiler
- if defined?(GC::Profiler)
- def with_gc_stats
- GC::Profiler.enable
- GC.start
- yield
- ensure
- GC::Profiler.disable
- end
- 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
- # Ruby 1.9
- if GC.respond_to?(:count)
- def measure
- GC.count
- end
- end
- end
-
- class GcTime < Time
- # Ruby 1.9 with GC::Profiler
- if defined?(GC::Profiler) && GC::Profiler.respond_to?(:total_time)
- def measure
- GC::Profiler.total_time
- end
- 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 22e41fa905..772c7b4209 100644
--- a/activesupport/lib/active_support/testing/setup_and_teardown.rb
+++ b/activesupport/lib/active_support/testing/setup_and_teardown.rb
@@ -10,11 +10,6 @@ module ActiveSupport
include ActiveSupport::Callbacks
define_callbacks :setup, :teardown
- if defined?(MiniTest::Assertions) && TestCase < MiniTest::Assertions
- include ForMiniTest
- else
- include ForClassicTestUnit
- end
end
module ClassMethods
@@ -27,83 +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
- end
-
- module ForClassicTestUnit
- # For compatibility with Ruby < 1.8.6
- PASSTHROUGH_EXCEPTIONS = Test::Unit::TestCase::PASSTHROUGH_EXCEPTIONS rescue [NoMemoryError, SignalException, Interrupt, SystemExit]
-
- # This redefinition is unfortunate but test/unit shows us no alternative.
- # Doubly unfortunate: hax to support Mocha's hax.
- def run(result)
- return if @method_name.to_s == "default_test"
-
- mocha_counter = retrieve_mocha_counter(result)
- yield(Test::Unit::TestCase::STARTED, name)
- @_result = result
-
- begin
- begin
- run_callbacks :setup do
- setup
- __send__(@method_name)
- mocha_verify(mocha_counter) if mocha_counter
- end
- rescue Mocha::ExpectationError => e
- add_failure(e.message, e.backtrace)
- rescue Test::Unit::AssertionFailedError => e
- add_failure(e.message, e.backtrace)
- rescue Exception => e
- raise if PASSTHROUGH_EXCEPTIONS.include?(e.class)
- add_error(e)
- ensure
- begin
- teardown
- run_callbacks :teardown
- rescue Test::Unit::AssertionFailedError => e
- add_failure(e.message, e.backtrace)
- rescue Exception => e
- raise if PASSTHROUGH_EXCEPTIONS.include?(e.class)
- add_error(e)
- end
- end
- ensure
- mocha_teardown if mocha_counter
- end
-
- result.add_run
- yield(Test::Unit::TestCase::FINISHED, name)
- end
-
- protected
-
- def retrieve_mocha_counter(result) #:nodoc:
- if respond_to?(:mocha_verify) # using mocha
- if defined?(Mocha::TestCaseAdapter::AssertionCounter)
- Mocha::TestCaseAdapter::AssertionCounter.new(result)
- else
- Mocha::Integration::TestUnit::AssertionCounter.new(result)
- end
end
end
+ result
end
end
diff --git a/activesupport/lib/active_support/time.rb b/activesupport/lib/active_support/time.rb
index 86f057d676..9634b52ecf 100644
--- a/activesupport/lib/active_support/time.rb
+++ b/activesupport/lib/active_support/time.rb
@@ -13,7 +13,6 @@ end
require 'date'
require 'time'
-require 'active_support/core_ext/time/publicize_conversion_methods'
require 'active_support/core_ext/time/marshal'
require 'active_support/core_ext/time/acts_like'
require 'active_support/core_ext/time/calculations'
@@ -21,7 +20,6 @@ require 'active_support/core_ext/time/conversions'
require 'active_support/core_ext/time/zones'
require 'active_support/core_ext/date/acts_like'
-require 'active_support/core_ext/date/freeze'
require 'active_support/core_ext/date/calculations'
require 'active_support/core_ext/date/conversions'
require 'active_support/core_ext/date/zones'
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/time_with_zone.rb b/activesupport/lib/active_support/time_with_zone.rb
index ec2c717942..1cb71012ef 100644
--- a/activesupport/lib/active_support/time_with_zone.rb
+++ b/activesupport/lib/active_support/time_with_zone.rb
@@ -1,4 +1,4 @@
-require "active_support/values/time_zone"
+require 'active_support/values/time_zone'
require 'active_support/core_ext/object/acts_like'
require 'active_support/core_ext/object/inclusion'
@@ -12,7 +12,7 @@ module ActiveSupport
#
# Time.zone = 'Eastern Time (US & Canada)' # => 'Eastern Time (US & Canada)'
# Time.zone.local(2007, 2, 10, 15, 30, 45) # => Sat, 10 Feb 2007 15:30:45 EST -05:00
- # Time.zone.parse('2007-02-01 15:30:45') # => Sat, 10 Feb 2007 15:30:45 EST -05:00
+ # Time.zone.parse('2007-02-10 15:30:45') # => Sat, 10 Feb 2007 15:30:45 EST -05:00
# Time.zone.at(1170361845) # => Sat, 10 Feb 2007 15:30:45 EST -05:00
# Time.zone.now # => Sun, 18 May 2008 13:07:55 EDT -04:00
# Time.utc(2007, 2, 10, 20, 30, 45).in_time_zone # => Sat, 10 Feb 2007 15:30:45 EST -05:00
@@ -146,12 +146,6 @@ module ActiveSupport
end
end
- def to_yaml(options = {})
- return super if defined?(YAML::ENGINE) && !YAML::ENGINE.syck?
-
- utc.to_yaml(options)
- end
-
def httpdate
utc.httpdate
end
@@ -203,7 +197,11 @@ module ActiveSupport
end
def eql?(other)
- utc == other
+ utc.eql?(other)
+ end
+
+ def hash
+ utc.hash
end
def +(other)
@@ -277,7 +275,6 @@ module ActiveSupport
def to_i
utc.to_i
end
- alias_method :hash, :to_i
alias_method :tv_sec, :to_i
# A TimeWithZone acts like a Time, so just return +self+.
diff --git a/activesupport/lib/active_support/values/time_zone.rb b/activesupport/lib/active_support/values/time_zone.rb
index 728921a069..0baf9e1914 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(':', '')
@@ -195,12 +196,8 @@ module ActiveSupport
# (GMT). Seconds were chosen as the offset unit because that is the unit that
# Ruby uses to represent time zone offsets (see Time#utc_offset).
def initialize(name, utc_offset = nil, tzinfo = nil)
- begin
- require 'tzinfo'
- rescue LoadError => e
- $stderr.puts "You don't have tzinfo installed in your application. Please add it to your Gemfile and run bundle install"
- raise e
- end
+ self.class.send(:require_tzinfo)
+
@name = name
@utc_offset = utc_offset
@tzinfo = tzinfo || TimeZone.find_tzinfo(name)
@@ -271,7 +268,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)
@@ -286,7 +283,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.
@@ -337,7 +334,12 @@ module ActiveSupport
end
def zones_map
- @zones_map ||= Hash[MAPPING.map { |place, _| [place, create(place)] }]
+ @zones_map ||= begin
+ new_zones_names = MAPPING.keys - lazy_zones_map.keys
+ new_zones = Hash[new_zones_names.map { |place| [place, create(place)] }]
+
+ lazy_zones_map.merge(new_zones)
+ end
end
# Locate a specific time zone object. If the argument is a string, it
@@ -349,7 +351,7 @@ module ActiveSupport
case arg
when String
begin
- zones_map[arg] ||= lookup(arg).tap { |tz| tz.utc_offset }
+ lazy_zones_map[arg] ||= lookup(arg).tap { |tz| tz.utc_offset }
rescue TZInfo::InvalidTimezoneIdentifier
nil
end
@@ -367,11 +369,34 @@ module ActiveSupport
@us_zones ||= all.find_all { |z| z.name =~ /US|Arizona|Indiana|Hawaii|Alaska/ }
end
+ protected
+
+ def require_tzinfo
+ require 'tzinfo' unless defined?(::TZInfo)
+ rescue LoadError
+ $stderr.puts "You don't have tzinfo installed in your application. Please add it to your Gemfile and run bundle install"
+ raise
+ end
+
private
def lookup(name)
(tzinfo = find_tzinfo(name)) && create(tzinfo.name.freeze)
end
+
+ def lazy_zones_map
+ require_tzinfo
+
+ @lazy_zones_map ||= Hash.new do |hash, place|
+ hash[place] = create(place) if MAPPING.has_key?(place)
+ 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
index 4fe0268cca..df17a8cccf 100644
--- a/activesupport/lib/active_support/values/unicode_tables.dat
+++ b/activesupport/lib/active_support/values/unicode_tables.dat
Binary files differ
diff --git a/activesupport/lib/active_support/version.rb b/activesupport/lib/active_support/version.rb
index bd8e7f907a..8a8f8f946d 100644
--- a/activesupport/lib/active_support/version.rb
+++ b/activesupport/lib/active_support/version.rb
@@ -1,7 +1,7 @@
module ActiveSupport
module VERSION #:nodoc:
- MAJOR = 3
- MINOR = 2
+ MAJOR = 4
+ MINOR = 0
TINY = 0
PRE = "beta"
diff --git a/activesupport/lib/active_support/whiny_nil.rb b/activesupport/lib/active_support/whiny_nil.rb
deleted file mode 100644
index 577db5018e..0000000000
--- a/activesupport/lib/active_support/whiny_nil.rb
+++ /dev/null
@@ -1,60 +0,0 @@
-# Extensions to +nil+ which allow for more helpful error messages for people who
-# are new to Rails.
-#
-# Ruby raises NoMethodError if you invoke a method on an object that does not
-# respond to it:
-#
-# $ ruby -e nil.destroy
-# -e:1: undefined method `destroy' for nil:NilClass (NoMethodError)
-#
-# With these extensions, if the method belongs to the public interface of the
-# classes in NilClass::WHINERS the error message suggests which could be the
-# actual intended class:
-#
-# $ rails runner nil.destroy
-# ...
-# You might have expected an instance of ActiveRecord::Base.
-# ...
-#
-# NilClass#id exists in Ruby 1.8 (though it is deprecated). Since +id+ is a fundamental
-# method of Active Record models NilClass#id is redefined as well to raise a RuntimeError
-# and warn the user. She probably wanted a model database identifier and the 4
-# returned by the original method could result in obscure bugs.
-#
-# The flag <tt>config.whiny_nils</tt> determines whether this feature is enabled.
-# By default it is on in development and test modes, and it is off in production
-# mode.
-class NilClass
- METHOD_CLASS_MAP = Hash.new
-
- def self.add_whiner(klass)
- methods = klass.public_instance_methods - public_instance_methods
- class_name = klass.name
- methods.each { |method| METHOD_CLASS_MAP[method.to_sym] = class_name }
- end
-
- add_whiner ::Array
-
- # Raises a RuntimeError when you attempt to call +id+ on +nil+.
- def id
- raise RuntimeError, "Called id for nil, which would mistakenly be #{object_id} -- if you really wanted the id of nil, use object_id", caller
- end
-
- private
- def method_missing(method, *args)
- if klass = METHOD_CLASS_MAP[method]
- raise_nil_warning_for klass, method, caller
- else
- super
- end
- end
-
- # Raises a NoMethodError when you attempt to call a method on +nil+.
- def raise_nil_warning_for(class_name = nil, selector = nil, with_caller = nil)
- message = "You have a nil object when you didn't expect it!"
- message << "\nYou might have expected an instance of #{class_name}." if class_name
- message << "\nThe error occurred while evaluating nil.#{selector}" if selector
-
- raise NoMethodError, message, with_caller || caller
- end
-end
diff --git a/activesupport/lib/active_support/xml_mini.rb b/activesupport/lib/active_support/xml_mini.rb
index 1ea9a9d7e1..677e9910bb 100644
--- a/activesupport/lib/active_support/xml_mini.rb
+++ b/activesupport/lib/active_support/xml_mini.rb
@@ -1,4 +1,5 @@
require 'time'
+require 'base64'
require 'active_support/core_ext/module/delegation'
require 'active_support/core_ext/string/inflections'
@@ -48,7 +49,7 @@ module ActiveSupport
"symbol" => Proc.new { |symbol| symbol.to_s },
"date" => Proc.new { |date| date.to_s(:db) },
"datetime" => Proc.new { |time| time.xmlschema },
- "binary" => Proc.new { |binary| ActiveSupport::Base64.encode64(binary) },
+ "binary" => Proc.new { |binary| ::Base64.encode64(binary) },
"yaml" => Proc.new { |yaml| yaml.to_yaml }
} unless defined?(FORMATTING)
@@ -64,7 +65,7 @@ module ActiveSupport
"boolean" => Proc.new { |boolean| %w(1 true).include?(boolean.strip) },
"string" => Proc.new { |string| string.to_s },
"yaml" => Proc.new { |yaml| YAML::load(yaml) rescue yaml },
- "base64Binary" => Proc.new { |bin| ActiveSupport::Base64.decode64(bin) },
+ "base64Binary" => Proc.new { |bin| ::Base64.decode64(bin) },
"binary" => Proc.new { |bin, entity| _parse_binary(bin, entity) },
"file" => Proc.new { |file, entity| _parse_file(file, entity) }
}
@@ -148,14 +149,14 @@ module ActiveSupport
def _parse_binary(bin, entity) #:nodoc:
case entity['encoding']
when 'base64'
- ActiveSupport::Base64.decode64(bin)
+ ::Base64.decode64(bin)
else
bin
end
end
def _parse_file(file, entity)
- f = StringIO.new(ActiveSupport::Base64.decode64(file))
+ f = StringIO.new(::Base64.decode64(file))
f.extend(FileLike)
f.original_filename = entity['name']
f.content_type = entity['content_type']
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