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.rb2
-rw-r--r--activesupport/lib/active_support/base64.rb6
-rw-r--r--activesupport/lib/active_support/benchmarkable.rb6
-rw-r--r--activesupport/lib/active_support/buffered_logger.rb1
-rw-r--r--activesupport/lib/active_support/cache.rb60
-rw-r--r--activesupport/lib/active_support/cache/file_store.rb7
-rw-r--r--activesupport/lib/active_support/cache/mem_cache_store.rb4
-rw-r--r--activesupport/lib/active_support/cache/strategy/local_cache.rb53
-rw-r--r--activesupport/lib/active_support/callbacks.rb364
-rw-r--r--activesupport/lib/active_support/concern.rb140
-rw-r--r--activesupport/lib/active_support/configurable.rb57
-rw-r--r--activesupport/lib/active_support/core_ext/array/conversions.rb2
-rw-r--r--activesupport/lib/active_support/core_ext/array/random_access.rb12
-rw-r--r--activesupport/lib/active_support/core_ext/array/wrap.rb2
-rw-r--r--activesupport/lib/active_support/core_ext/big_decimal/conversions.rb19
-rw-r--r--activesupport/lib/active_support/core_ext/cgi.rb1
-rw-r--r--activesupport/lib/active_support/core_ext/cgi/escape_skipping_slashes.rb19
-rw-r--r--activesupport/lib/active_support/core_ext/class/attribute.rb36
-rw-r--r--activesupport/lib/active_support/core_ext/class/attribute_accessors.rb4
-rw-r--r--activesupport/lib/active_support/core_ext/class/inheritable_attributes.rb106
-rw-r--r--activesupport/lib/active_support/core_ext/class/subclasses.rb54
-rw-r--r--activesupport/lib/active_support/core_ext/date/calculations.rb24
-rw-r--r--activesupport/lib/active_support/core_ext/date/conversions.rb8
-rw-r--r--activesupport/lib/active_support/core_ext/date/zones.rb6
-rw-r--r--activesupport/lib/active_support/core_ext/date_time/calculations.rb36
-rw-r--r--activesupport/lib/active_support/core_ext/date_time/conversions.rb16
-rw-r--r--activesupport/lib/active_support/core_ext/date_time/zones.rb2
-rw-r--r--activesupport/lib/active_support/core_ext/enumerable.rb15
-rw-r--r--activesupport/lib/active_support/core_ext/float/rounding.rb2
-rw-r--r--activesupport/lib/active_support/core_ext/hash.rb1
-rw-r--r--activesupport/lib/active_support/core_ext/hash/conversions.rb48
-rw-r--r--activesupport/lib/active_support/core_ext/hash/deep_dup.rb11
-rw-r--r--activesupport/lib/active_support/core_ext/hash/indifferent_access.rb4
-rw-r--r--activesupport/lib/active_support/core_ext/hash/keys.rb10
-rw-r--r--activesupport/lib/active_support/core_ext/hash/reverse_merge.rb23
-rw-r--r--activesupport/lib/active_support/core_ext/integer/time.rb4
-rw-r--r--activesupport/lib/active_support/core_ext/kernel/reporting.rb21
-rw-r--r--activesupport/lib/active_support/core_ext/logger.rb49
-rw-r--r--activesupport/lib/active_support/core_ext/module/anonymous.rb2
-rw-r--r--activesupport/lib/active_support/core_ext/module/attr_accessor_with_default.rb27
-rw-r--r--activesupport/lib/active_support/core_ext/module/attr_internal.rb19
-rw-r--r--activesupport/lib/active_support/core_ext/module/attribute_accessors.rb2
-rw-r--r--activesupport/lib/active_support/core_ext/module/delegation.rb2
-rw-r--r--activesupport/lib/active_support/core_ext/module/deprecation.rb2
-rw-r--r--activesupport/lib/active_support/core_ext/module/remove_method.rb2
-rw-r--r--activesupport/lib/active_support/core_ext/module/synchronization.rb5
-rw-r--r--activesupport/lib/active_support/core_ext/numeric/time.rb22
-rw-r--r--activesupport/lib/active_support/core_ext/object.rb1
-rw-r--r--activesupport/lib/active_support/core_ext/object/blank.rb4
-rw-r--r--activesupport/lib/active_support/core_ext/object/instance_variables.rb40
-rw-r--r--activesupport/lib/active_support/core_ext/object/returning.rb43
-rw-r--r--activesupport/lib/active_support/core_ext/object/to_param.rb22
-rw-r--r--activesupport/lib/active_support/core_ext/object/to_query.rb4
-rw-r--r--activesupport/lib/active_support/core_ext/object/try.rb19
-rw-r--r--activesupport/lib/active_support/core_ext/object/with_options.rb24
-rw-r--r--activesupport/lib/active_support/core_ext/range.rb1
-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/string.rb3
-rw-r--r--activesupport/lib/active_support/core_ext/string/access.rb14
-rw-r--r--activesupport/lib/active_support/core_ext/string/filters.rb6
-rw-r--r--activesupport/lib/active_support/core_ext/string/inflections.rb16
-rw-r--r--activesupport/lib/active_support/core_ext/string/multibyte.rb2
-rw-r--r--activesupport/lib/active_support/core_ext/string/output_safety.rb27
-rw-r--r--activesupport/lib/active_support/core_ext/string/strip.rb26
-rw-r--r--activesupport/lib/active_support/core_ext/time/calculations.rb33
-rw-r--r--activesupport/lib/active_support/core_ext/time/conversions.rb30
-rw-r--r--activesupport/lib/active_support/core_ext/time/zones.rb18
-rw-r--r--activesupport/lib/active_support/core_ext/uri.rb8
-rw-r--r--activesupport/lib/active_support/dependencies.rb190
-rw-r--r--activesupport/lib/active_support/deprecation.rb4
-rw-r--r--activesupport/lib/active_support/deprecation/behaviors.rb7
-rw-r--r--activesupport/lib/active_support/deprecation/proxy_wrappers.rb4
-rw-r--r--activesupport/lib/active_support/deprecation/reporting.rb18
-rw-r--r--activesupport/lib/active_support/duration.rb8
-rw-r--r--activesupport/lib/active_support/file_update_checker.rb2
-rw-r--r--activesupport/lib/active_support/file_watcher.rb36
-rw-r--r--activesupport/lib/active_support/gzip.rb6
-rw-r--r--activesupport/lib/active_support/hash_with_indifferent_access.rb28
-rw-r--r--activesupport/lib/active_support/i18n.rb2
-rw-r--r--activesupport/lib/active_support/i18n_railtie.rb29
-rw-r--r--activesupport/lib/active_support/inflections.rb4
-rw-r--r--activesupport/lib/active_support/inflector/inflections.rb2
-rw-r--r--activesupport/lib/active_support/inflector/transliterate.rb1
-rw-r--r--activesupport/lib/active_support/json/backends/jsongem.rb6
-rw-r--r--activesupport/lib/active_support/json/backends/yajl.rb6
-rw-r--r--activesupport/lib/active_support/json/backends/yaml.rb45
-rw-r--r--activesupport/lib/active_support/json/encoding.rb57
-rw-r--r--activesupport/lib/active_support/lazy_load_hooks.rb18
-rw-r--r--activesupport/lib/active_support/locale/en.yml6
-rw-r--r--activesupport/lib/active_support/log_subscriber.rb71
-rw-r--r--activesupport/lib/active_support/log_subscriber/test_helper.rb9
-rw-r--r--activesupport/lib/active_support/memoizable.rb2
-rw-r--r--activesupport/lib/active_support/message_encryptor.rb36
-rw-r--r--activesupport/lib/active_support/message_verifier.rb16
-rw-r--r--activesupport/lib/active_support/multibyte.rb2
-rw-r--r--activesupport/lib/active_support/multibyte/chars.rb9
-rw-r--r--activesupport/lib/active_support/multibyte/unicode.rb2
-rw-r--r--activesupport/lib/active_support/notifications.rb17
-rw-r--r--activesupport/lib/active_support/ordered_hash.rb29
-rw-r--r--activesupport/lib/active_support/ordered_options.rb30
-rw-r--r--activesupport/lib/active_support/rescuable.rb1
-rw-r--r--activesupport/lib/active_support/secure_random.rb16
-rw-r--r--activesupport/lib/active_support/test_case.rb3
-rw-r--r--activesupport/lib/active_support/testing/assertions.rb14
-rw-r--r--activesupport/lib/active_support/testing/declarative.rb8
-rw-r--r--activesupport/lib/active_support/testing/default.rb9
-rw-r--r--activesupport/lib/active_support/testing/isolation.rb2
-rw-r--r--activesupport/lib/active_support/testing/pending.rb10
-rw-r--r--activesupport/lib/active_support/testing/performance.rb10
-rw-r--r--activesupport/lib/active_support/testing/setup_and_teardown.rb8
-rw-r--r--activesupport/lib/active_support/time_with_zone.rb24
-rw-r--r--activesupport/lib/active_support/version.rb6
-rw-r--r--activesupport/lib/active_support/whiny_nil.rb2
-rw-r--r--activesupport/lib/active_support/xml_mini.rb8
-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.rb4
-rw-r--r--activesupport/lib/active_support/xml_mini/rexml.rb3
120 files changed, 1448 insertions, 1051 deletions
diff --git a/activesupport/lib/active_support/backtrace_cleaner.rb b/activesupport/lib/active_support/backtrace_cleaner.rb
index 8465bc1e10..0e6bc30fa2 100644
--- a/activesupport/lib/active_support/backtrace_cleaner.rb
+++ b/activesupport/lib/active_support/backtrace_cleaner.rb
@@ -8,7 +8,7 @@ module ActiveSupport
# 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.
#
- # Example:
+ # ==== Example:
#
# bc = BacktraceCleaner.new
# bc.add_filter { |line| line.gsub(Rails.root, '') }
diff --git a/activesupport/lib/active_support/base64.rb b/activesupport/lib/active_support/base64.rb
index b8c01628ce..35014cb3d5 100644
--- a/activesupport/lib/active_support/base64.rb
+++ b/activesupport/lib/active_support/base64.rb
@@ -7,7 +7,7 @@ module ActiveSupport
if defined? ::Base64
Base64 = ::Base64
else
- # Base64 provides utility methods for encoding and de-coding binary data
+ # 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.
@@ -15,7 +15,7 @@ module ActiveSupport
# 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")
+ # ActiveSupport::Base64.encode64("Original unencoded string")
# # => "T3JpZ2luYWwgdW5lbmNvZGVkIHN0cmluZw==\n"
def self.encode64(data)
[data].pack("m")
@@ -23,7 +23,7 @@ module ActiveSupport
# Decodes a base 64 encoded string to its original representation.
#
- # ActiveSupport::Base64.decode64("T3JpZ2luYWwgdW5lbmNvZGVkIHN0cmluZw==")
+ # ActiveSupport::Base64.decode64("T3JpZ2luYWwgdW5lbmNvZGVkIHN0cmluZw==")
# # => "Original unencoded string"
def self.decode64(data)
data.unpack("m").first
diff --git a/activesupport/lib/active_support/benchmarkable.rb b/activesupport/lib/active_support/benchmarkable.rb
index ee02ecb043..df62c18f41 100644
--- a/activesupport/lib/active_support/benchmarkable.rb
+++ b/activesupport/lib/active_support/benchmarkable.rb
@@ -3,10 +3,10 @@ require 'active_support/core_ext/hash/keys'
module ActiveSupport
module Benchmarkable
- # Allows you to measure the execution time of a block
+ # Allows you to measure the execution time of a block
# in a template and records the result to the log. Wrap this block around
# expensive operations or possible bottlenecks to get a time reading
- # for the operation. For example, let's say you thought your file
+ # for the operation. For example, let's say you thought your file
# processing method was taking too long; you could wrap it in a benchmark block.
#
# <% benchmark "Process data files" do %>
@@ -23,7 +23,7 @@ module ActiveSupport
# <%= lowlevel_files_operation %>
# <% end %>
#
- # Finally, you can pass true as the third argument to silence all log activity
+ # Finally, you can pass true as the third argument to silence all log activity
# inside the block. This is great for boiling down a noisy block to just a single statement:
#
# <% benchmark "Process data files", :level => :info, :silence => true do %>
diff --git a/activesupport/lib/active_support/buffered_logger.rb b/activesupport/lib/active_support/buffered_logger.rb
index b861a6f62a..e41731f3e7 100644
--- a/activesupport/lib/active_support/buffered_logger.rb
+++ b/activesupport/lib/active_support/buffered_logger.rb
@@ -1,3 +1,4 @@
+require 'thread'
require 'active_support/core_ext/class/attribute_accessors'
module ActiveSupport
diff --git a/activesupport/lib/active_support/cache.rb b/activesupport/lib/active_support/cache.rb
index 30195bdea5..10c457bb1d 100644
--- a/activesupport/lib/active_support/cache.rb
+++ b/activesupport/lib/active_support/cache.rb
@@ -58,7 +58,14 @@ module ActiveSupport
case store
when Symbol
store_class_name = store.to_s.camelize
- store_class = ActiveSupport::Cache.const_get(store_class_name)
+ 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
@@ -200,26 +207,26 @@ module ActiveSupport
#
# Setting <tt>:expires_in</tt> will set an expiration time on the cache. All caches
# support auto expiring content after a specified number of seconds. This value can
- # be specified as an option to the construction in which call all entries will be
+ # be specified as an option to the construction in which call all entries will be
# affected. Or it can be supplied to the +fetch+ or +write+ method for just one entry.
#
- # cache = ActiveSupport::Cache::MemoryStore.new(:expire_in => 5.minutes)
- # cache.write(key, value, :expire_in => 1.minute) # Set a lower value for one entry
+ # cache = ActiveSupport::Cache::MemoryStore.new(:expires_in => 5.minutes)
+ # cache.write(key, value, :expires_in => 1.minute) # Set a lower value for one entry
#
# Setting <tt>:race_condition_ttl</tt> is very useful in situations where a cache entry
- # is used very frequently unver heavy load. If a cache expires and due to heavy load
- # seven different processes will try to read data natively and then they all will try to
- # write to cache. To avoid that case the first process to find an expired cache entry will
+ # is used very frequently and is under heavy load. If a cache expires and due to heavy load
+ # seven different processes will try to read data natively and then they all will try to
+ # write to cache. To avoid that case the first process to find an expired cache entry will
# bump the cache expiration time by the value set in <tt>:race_condition_ttl</tt>. Yes
- # this process is extending the time for a stale value by another few seconds. Because
+ # this process is extending the time for a stale value by another few seconds. Because
# of extended life of the previous cache, other processes will continue to use slightly
# stale data for a just a big longer. In the meantime that first process will go ahead
# and will write into cache the new value. After that all the processes will start
# getting new value. The key is to keep <tt>:race_condition_ttl</tt> small.
#
# If the process regenerating the entry errors out, the entry will be regenerated
- # after the specified number of seconds. Also note that the life of stale cache is
- # extended only if it expired recently. Otherwise a new value is generated and
+ # after the specified number of seconds. Also note that the life of stale cache is
+ # extended only if it expired recently. Otherwise a new value is generated and
# <tt>:race_condition_ttl</tt> does not play any role.
#
# # Set all values to expire after one minute.
@@ -410,7 +417,7 @@ module ActiveSupport
raise NotImplementedError.new("#{self.class.name} does not support decrement")
end
- # Cleanup the cache by removing expired entries.
+ # Cleanup the cache by removing expired entries.
#
# Options are passed to the underlying cache implementation.
#
@@ -419,7 +426,7 @@ module ActiveSupport
raise NotImplementedError.new("#{self.class.name} does not support cleanup")
end
- # Clear the entire cache. Be careful with this method since it could
+ # Clear the entire cache. Be careful with this method since it could
# affect other processes if shared cache is being used.
#
# Options are passed to the underlying cache implementation.
@@ -473,23 +480,24 @@ module ActiveSupport
end
end
- # Expand key to be a consistent string value. Invoke +cache_key+ if
- # object responds to +cache_key+. Otherwise, to_param method will be
+ # Expand key to be a consistent string value. Invoke +cache_key+ if
+ # object responds to +cache_key+. Otherwise, to_param method will be
# called. If the key is a Hash, then keys will be sorted alphabetically.
def expanded_key(key) # :nodoc:
- if key.respond_to?(:cache_key)
- key = key.cache_key.to_s
- elsif key.is_a?(Array)
+ return key.cache_key.to_s if key.respond_to?(:cache_key)
+
+ case key
+ when Array
if key.size > 1
- key.collect{|element| expanded_key(element)}.to_param
+ key = key.collect{|element| expanded_key(element)}
else
- key.first.to_param
+ key = key.first
end
- elsif key.is_a?(Hash)
- key = key.to_a.sort{|a,b| a.first.to_s <=> b.first.to_s}.collect{|k,v| "#{k}=#{v}"}.to_param
- else
- key = key.to_param
+ when Hash
+ key = key.sort_by { |k,_| k.to_s }.collect{|k,v| "#{k}=#{v}"}
end
+
+ key.to_param
end
# Prefix a key with the namespace. Namespace and key will be delimited with a colon.
@@ -582,11 +590,7 @@ module ActiveSupport
# Check if the entry is expired. The +expires_in+ parameter can override the
# value set when the entry was created.
def expired?
- if @expires_in && @created_at + @expires_in <= Time.now.to_f
- true
- else
- false
- end
+ @expires_in && @created_at + @expires_in <= Time.now.to_f
end
# Set a new time when the entry will expire.
diff --git a/activesupport/lib/active_support/cache/file_store.rb b/activesupport/lib/active_support/cache/file_store.rb
index 84f6f29572..18182bbb40 100644
--- a/activesupport/lib/active_support/cache/file_store.rb
+++ b/activesupport/lib/active_support/cache/file_store.rb
@@ -1,5 +1,6 @@
require 'active_support/core_ext/file/atomic'
require 'active_support/core_ext/string/conversions'
+require 'rack/utils'
module ActiveSupport
module Cache
@@ -11,8 +12,6 @@ module ActiveSupport
attr_reader :cache_path
DIR_FORMATTER = "%03X"
- ESCAPE_FILENAME_CHARS = /[^a-z0-9_.-]/i
- UNESCAPE_FILENAME_CHARS = /%[0-9A-F]{2}/
def initialize(cache_path, options = nil)
super(options)
@@ -136,7 +135,7 @@ module ActiveSupport
# Translate a key into a file path.
def key_file_path(key)
- fname = key.to_s.gsub(ESCAPE_FILENAME_CHARS){|match| "%#{match.ord.to_s(16).upcase}"}
+ fname = Rack::Utils.escape(key)
hash = Zlib.adler32(fname)
hash, dir_1 = hash.divmod(0x1000)
dir_2 = hash.modulo(0x1000)
@@ -156,7 +155,7 @@ module ActiveSupport
# Translate a file path into a key.
def file_path_key(path)
fname = path[cache_path.size, path.size].split(File::SEPARATOR, 4).last
- fname.gsub(UNESCAPE_FILENAME_CHARS){|match| $1.ord.to_s(16)}
+ Rack::Utils.unescape(fname)
end
# Delete empty directories in the cache.
diff --git a/activesupport/lib/active_support/cache/mem_cache_store.rb b/activesupport/lib/active_support/cache/mem_cache_store.rb
index f32b562368..45263d482f 100644
--- a/activesupport/lib/active_support/cache/mem_cache_store.rb
+++ b/activesupport/lib/active_support/cache/mem_cache_store.rb
@@ -1,7 +1,7 @@
begin
require 'memcache'
rescue LoadError => e
- $stderr.puts "You don't have memcache installed in your application. Please add it to your Gemfile and run bundle install"
+ $stderr.puts "You don't have memcache-client installed in your application. Please add it to your Gemfile and run bundle install"
raise e
end
require 'digest/md5'
@@ -73,7 +73,7 @@ module ActiveSupport
def read_multi(*names)
options = names.extract_options!
options = merged_options(options)
- keys_to_names = names.inject({}){|map, name| map[escape_key(namespaced_key(name, options))] = name; map}
+ keys_to_names = Hash[names.map{|name| [escape_key(namespaced_key(name, options)), name]}]
raw_values = @data.get_multi(keys_to_names.keys, :raw => true)
values = {}
raw_values.each do |key, value|
diff --git a/activesupport/lib/active_support/cache/strategy/local_cache.rb b/activesupport/lib/active_support/cache/strategy/local_cache.rb
index 3edba52fc4..0649a058aa 100644
--- a/activesupport/lib/active_support/cache/strategy/local_cache.rb
+++ b/activesupport/lib/active_support/cache/strategy/local_cache.rb
@@ -16,7 +16,7 @@ module ActiveSupport
@data = {}
end
- # Don't allow synchronizing since it isn't thread safe,
+ # Don't allow synchronizing since it isn't thread safe,
def synchronize # :nodoc:
yield
end
@@ -50,34 +50,39 @@ module ActiveSupport
end
end
- # Middleware class can be inserted as a Rack handler to be local cache for the
- # duration of request.
- def middleware
- @middleware ||= begin
- klass = Class.new
- klass.class_eval(<<-EOS, __FILE__, __LINE__ + 1)
- class << self
- def name
- "ActiveSupport::Cache::Strategy::LocalCache"
- end
- alias :to_s :name
- end
+ #--
+ # This class wraps up local storage for middlewares. Only the middleware method should
+ # construct them.
+ class Middleware # :nodoc:
+ attr_reader :name, :thread_local_key
- def initialize(app)
- @app = app
- end
+ def initialize(name, thread_local_key)
+ @name = name
+ @thread_local_key = thread_local_key
+ @app = nil
+ end
- def call(env)
- Thread.current[:#{thread_local_key}] = LocalStore.new
- @app.call(env)
- ensure
- Thread.current[:#{thread_local_key}] = nil
- end
- EOS
- klass
+ def new(app)
+ @app = app
+ self
+ end
+
+ def call(env)
+ Thread.current[thread_local_key] = LocalStore.new
+ @app.call(env)
+ ensure
+ Thread.current[thread_local_key] = nil
end
end
+ # Middleware class can be inserted as a Rack handler to be local cache for the
+ # duration of request.
+ def middleware
+ @middleware ||= Middleware.new(
+ "ActiveSupport::Cache::Strategy::LocalCache",
+ thread_local_key)
+ end
+
def clear(options = nil) # :nodoc:
local_cache.clear(options) if local_cache
super
diff --git a/activesupport/lib/active_support/callbacks.rb b/activesupport/lib/active_support/callbacks.rb
index 24e407c253..418102352f 100644
--- a/activesupport/lib/active_support/callbacks.rb
+++ b/activesupport/lib/active_support/callbacks.rb
@@ -1,31 +1,33 @@
+require 'active_support/concern'
require 'active_support/descendants_tracker'
require 'active_support/core_ext/array/wrap'
-require 'active_support/core_ext/class/inheritable_attributes'
+require 'active_support/core_ext/class/attribute'
require 'active_support/core_ext/kernel/reporting'
require 'active_support/core_ext/kernel/singleton_class'
module ActiveSupport
- # Callbacks are hooks into the lifecycle of an object that allow you to trigger logic
- # before or after an alteration of the object state.
+ # \Callbacks are code hooks that are run at key points in an object's lifecycle.
+ # The typical use case is to have a base class define a set of callbacks relevant
+ # to the other functionality it supplies, so that subclasses can install callbacks
+ # that enhance or modify the base functionality without needing to override
+ # or redefine methods of the base class.
#
- # Mixing in this module allows you to define callbacks in your class.
+ # Mixing in this module allows you to define the events in the object's lifecycle
+ # that will support callbacks (via +ClassMethods.define_callbacks+), set the instance
+ # methods, procs, or callback objects to be called (via +ClassMethods.set_callback+),
+ # and run the installed callbacks at the appropriate times (via +run_callbacks+).
#
- # Example:
- # class Storage
- # include ActiveSupport::Callbacks
- #
- # define_callbacks :save
- # end
+ # Three kinds of callbacks are supported: before callbacks, run before a certain event;
+ # after callbacks, run after the event; and around callbacks, blocks that surround the
+ # event, triggering it when they yield. Callback code can be contained in instance
+ # methods, procs or lambdas, or callback objects that respond to certain predetermined
+ # methods. See +ClassMethods.set_callback+ for details.
#
- # class ConfigStorage < Storage
- # set_callback :save, :before, :saving_message
- # def saving_message
- # puts "saving..."
- # end
+ # ==== Example
#
- # set_callback :save, :after do |object|
- # puts "saved"
- # end
+ # class Record
+ # include ActiveSupport::Callbacks
+ # define_callbacks :save
#
# def save
# run_callbacks :save do
@@ -34,29 +36,7 @@ module ActiveSupport
# end
# end
#
- # config = ConfigStorage.new
- # config.save
- #
- # Output:
- # saving...
- # - save
- # saved
- #
- # Callbacks from parent classes are inherited.
- #
- # Example:
- # class Storage
- # include ActiveSupport::Callbacks
- #
- # define_callbacks :save
- #
- # set_callback :save, :before, :prepare
- # def prepare
- # puts "preparing save"
- # end
- # end
- #
- # class ConfigStorage < Storage
+ # class PersonRecord < Record
# set_callback :save, :before, :saving_message
# def saving_message
# puts "saving..."
@@ -65,19 +45,12 @@ module ActiveSupport
# set_callback :save, :after do |object|
# puts "saved"
# end
- #
- # def save
- # run_callbacks :save do
- # puts "- save"
- # end
- # end
# end
#
- # config = ConfigStorage.new
- # config.save
+ # person = PersonRecord.new
+ # person.save
#
# Output:
- # preparing save
# saving...
# - save
# saved
@@ -89,11 +62,25 @@ module ActiveSupport
extend ActiveSupport::DescendantsTracker
end
+ # Runs the callbacks for the given event.
+ #
+ # 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.
+ #
+ # run_callbacks :save do
+ # save
+ # end
+ #
def run_callbacks(kind, *args, &block)
send("_run_#{kind}_callbacks", *args, &block)
end
- class Callback
+ class Callback #:nodoc:#
@@_callback_sequence = 0
attr_accessor :chain, :filter, :kind, :options, :per_key, :klass, :raw_filter
@@ -178,49 +165,52 @@ module ActiveSupport
# options[0] is the compiled form of supplied conditions
# options[1] is the "end" for the conditional
#
- if @kind == :before || @kind == :around
- if @kind == :before
- # if condition # before_save :filter_name, :if => :condition
- # filter_name
- # end
- filter = <<-RUBY_EVAL
- unless halted
- result = #{@filter}
- halted = (#{chain.config[:terminator]})
- end
- RUBY_EVAL
-
- [@compiled_options[0], filter, @compiled_options[1]].compact.join("\n")
- else
- # 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
+ 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.
+ 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
end
- RUBY_EVAL
- "#{name}(halted) do"
- end
+ end
+ RUBY_EVAL
+ "#{name}(halted) do"
end
end
@@ -229,15 +219,17 @@ module ActiveSupport
def end(key=nil, object=nil)
return if key && !object.send("_one_time_conditions_valid_#{@callback_id}?")
- if @kind == :around || @kind == :after
+ case @kind
+ when :after
# if condition # after_save :filter_name, :if => :condition
# filter_name
# end
- if @kind == :after
- [@compiled_options[0], @filter, @compiled_options[1]].compact.join("\n")
- else
- "end"
+ [@compiled_options[0], @filter, @compiled_options[1]].compact.join("\n")
+ when :around
+ <<-RUBY_EVAL
+ value
end
+ RUBY_EVAL
end
end
@@ -330,7 +322,7 @@ module ActiveSupport
end
# An Array with a compile method
- class CallbackChain < Array
+ class CallbackChain < Array #:nodoc:#
attr_reader :name, :config
def initialize(name, config)
@@ -375,20 +367,9 @@ module ActiveSupport
end
module ClassMethods
- # Make the run_callbacks :save method. The generated method takes
- # a block that it'll yield to. It'll call the before and around filters
- # in order, yield the block, and then run the after filters.
- #
- # run_callbacks :save do
- # save
- # end
- #
- # The run_callbacks :save method can optionally take a key, which
- # will be used to compile an optimized callback method for each
- # key. See #define_callbacks for more information.
- #
+ # Generate the internal runner method called by +run_callbacks+.
def __define_runner(symbol) #:nodoc:
- body = send("_#{symbol}_callbacks").compile(nil)
+ body = send("_#{symbol}_callbacks").compile
silence_warnings do
undef_method "_run_#{symbol}_callbacks" if method_defined?("_run_#{symbol}_callbacks")
@@ -435,21 +416,52 @@ module ActiveSupport
options = filters.last.is_a?(Hash) ? filters.pop : {}
filters.unshift(block) if block
- ([self] + ActiveSupport::DescendantsTracker.descendants(self)).each do |target|
+ ([self] + ActiveSupport::DescendantsTracker.descendants(self)).reverse.each do |target|
chain = target.send("_#{name}_callbacks")
- yield chain, type, filters, options
+ yield target, chain.dup, type, filters, options
target.__define_runner(name)
end
end
- # Set callbacks for a previously defined callback.
+ # Install a callback for the given event.
#
- # Syntax:
# set_callback :save, :before, :before_meth
# set_callback :save, :after, :after_meth, :if => :condition
- # set_callback :save, :around, lambda { |r| stuff; yield; stuff }
+ # set_callback :save, :around, lambda { |r| stuff; result = yield; stuff }
+ #
+ # The second arguments indicates whether the callback is to be run +:before+,
+ # +:after+, or +:around+ the event. If omitted, +:before+ is assumed. This
+ # means the first example above can also be written as:
+ #
+ # set_callback :save, :before_meth
+ #
+ # The callback can specified as a symbol naming an instance method; as a proc,
+ # lambda, or block; as a string to be instance evaluated; or as an object that
+ # responds to a certain method determined by the <tt>:scope</tt> argument to
+ # +define_callback+.
#
- # Use skip_callback to skip any defined one.
+ # If a proc, lambda, or block is given, its body is evaluated in the context
+ # of the current object. It can also optionally accept the current object as
+ # an argument.
+ #
+ # Before and around callbacks are called in the order that they are set; after
+ # callbacks are called in the reverse order.
+ #
+ # Around callbacks can access the return value from the event, if it
+ # wasn't halted, from the +yield+ call.
+ #
+ # ===== Options
+ #
+ # * <tt>:if</tt> - A symbol naming an instance method or a proc; the callback
+ # will be called only when it returns a true value.
+ # * <tt>:unless</tt> - A symbol naming an instance method or a proc; the callback
+ # 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,
@@ -459,12 +471,12 @@ module ActiveSupport
#
# becomes
#
- # dispatch_callback :before, :authenticate, :per_key => {:unless => proc {|c| c.action_name == "index"}}
+ # 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.
+ # 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(:dispatch, action_name) { ... dispatch stuff ... }
+ # 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
@@ -473,27 +485,30 @@ module ActiveSupport
def set_callback(name, *filter_list, &block)
mapped = nil
- __update_callbacks(name, filter_list, block) do |chain, type, filters, options|
+ __update_callbacks(name, filter_list, block) do |target, chain, type, filters, options|
mapped ||= filters.map do |filter|
Callback.new(chain, filter, type, options.dup, self)
end
filters.each do |filter|
- chain.delete_if {|c| c.matches?(type, filter) }
+ chain.delete_if {|c| c.matches?(type, filter) }
end
- options[:prepend] ? chain.unshift(*mapped) : chain.push(*mapped)
+ options[:prepend] ? chain.unshift(*(mapped.reverse)) : chain.push(*mapped)
+
+ target.send("_#{name}_callbacks=", chain)
end
end
- # Skip a previously defined callback.
+ # Skip a previously set callback. Like +set_callback+, <tt>:if</tt> or <tt>:unless</tt>
+ # options may be passed in order to control when the callback is skipped.
#
# class Writer < Person
# skip_callback :validate, :before, :check_membership, :if => lambda { self.age > 18 }
# end
#
def skip_callback(name, *filter_list, &block)
- __update_callbacks(name, filter_list, block) do |chain, type, filters, options|
+ __update_callbacks(name, filter_list, block) do |target, chain, type, filters, options|
filters.each do |filter|
filter = chain.find {|c| c.matches?(type, filter) }
@@ -505,93 +520,98 @@ module ActiveSupport
chain.delete(filter)
end
+ target.send("_#{name}_callbacks=", chain)
end
end
- # Reset callbacks for a given type.
+ # Remove all set callbacks for the given event.
#
def reset_callbacks(symbol)
callbacks = send("_#{symbol}_callbacks")
ActiveSupport::DescendantsTracker.descendants(self).each do |target|
- chain = target.send("_#{symbol}_callbacks")
+ chain = target.send("_#{symbol}_callbacks").dup
callbacks.each { |c| chain.delete(c) }
+ target.send("_#{symbol}_callbacks=", chain)
target.__define_runner(symbol)
end
- callbacks.clear
+ self.send("_#{symbol}_callbacks=", callbacks.dup.clear)
+
__define_runner(symbol)
end
- # Defines callbacks types:
+ # Define sets of events in the object lifecycle that support callbacks.
#
# define_callbacks :validate
+ # define_callbacks :initialize, :save, :destroy
#
- # This macro accepts the following options:
+ # ===== Options
#
- # * <tt>:terminator</tt> - Indicates when a before filter is considered
- # to halted. This is a string to be eval'ed and has the result of the
- # very filter available in the <tt>result</tt> variable:
+ # * <tt>:terminator</tt> - Determines when a before filter will halt the callback
+ # chain, preventing following callbacks from being called and the event from being
+ # triggered. This is a string to be eval'ed. The result of the callback is available
+ # in the <tt>result</tt> variable.
#
- # define_callbacks :validate, :terminator => "result == false"
+ # define_callbacks :validate, :terminator => "result == false"
#
- # In the example above, if any before validate callbacks returns +false+,
- # other callbacks are not executed. Defaults to "false", meaning no value
- # halts the chain.
+ # In this example, if any before validate callbacks returns +false+,
+ # 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. Set this option to
- # true to change this behavior.
+ # 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>:scope</tt> - Indicates which methods should be executed when a class
- # is given as callback. Defaults to <tt>[:kind]</tt>.
+ # * <tt>:scope</tt> - Indicates which methods should be executed when an object
+ # is used as a callback.
#
- # class Audit
- # def before(caller)
- # puts 'Audit: before is called'
- # end
+ # class Audit
+ # def before(caller)
+ # puts 'Audit: before is called'
+ # end
#
- # def before_save(caller)
- # puts 'Audit: before_save is called'
- # end
- # end
+ # def before_save(caller)
+ # puts 'Audit: before_save is called'
+ # end
+ # end
#
- # class Account
- # include ActiveSupport::Callbacks
+ # class Account
+ # include ActiveSupport::Callbacks
#
- # define_callbacks :save
- # set_callback :save, :before, Audit.new
+ # define_callbacks :save
+ # set_callback :save, :before, Audit.new
#
- # def save
- # run_callbacks :save do
- # puts 'save in main'
- # end
- # end
- # end
+ # def save
+ # run_callbacks :save do
+ # puts 'save in main'
+ # end
+ # end
+ # end
#
- # In the above case whenever you save an account the method <tt>Audit#before</tt> will
- # be called. On the other hand
+ # In the above case whenever you save an account the method <tt>Audit#before</tt> will
+ # be called. On the other hand
#
- # define_callbacks :save, :scope => [:kind, :name]
+ # define_callbacks :save, :scope => [:kind, :name]
#
- # would trigger <tt>Audit#before_save</tt> instead. That's constructed by calling
- # <tt>"#{kind}_#{name}"</tt> on the given instance. In this case "kind" is "before" and
- # "name" is "save". In this context ":kind" and ":name" have special meanings: ":kind"
- # refers to the kind of callback (before/after/around) and ":name" refers to the
- # method on which callbacks are being defined.
+ # would trigger <tt>Audit#before_save</tt> instead. That's constructed by calling
+ # <tt>#{kind}_#{name}</tt> on the given instance. In this case "kind" is "before" and
+ # "name" is "save". In this context +:kind+ and +:name+ have special meanings: +:kind+
+ # refers to the kind of callback (before/after/around) and +:name+ refers to the
+ # method on which callbacks are being defined.
#
- # A declaration like
+ # A declaration like
#
- # define_callbacks :save, :scope => [:name]
+ # define_callbacks :save, :scope => [:name]
#
- # would call <tt>Audit#save</tt>.
+ # would call <tt>Audit#save</tt>.
#
def define_callbacks(*callbacks)
config = callbacks.last.is_a?(Hash) ? callbacks.pop : {}
callbacks.each do |callback|
- extlib_inheritable_reader("_#{callback}_callbacks") do
- CallbackChain.new(callback, config)
- end
+ class_attribute "_#{callback}_callbacks"
+ send("_#{callback}_callbacks=", CallbackChain.new(callback, config))
__define_runner(callback)
end
end
diff --git a/activesupport/lib/active_support/concern.rb b/activesupport/lib/active_support/concern.rb
index 2d87e8d0e5..81fb859334 100644
--- a/activesupport/lib/active_support/concern.rb
+++ b/activesupport/lib/active_support/concern.rb
@@ -1,39 +1,109 @@
-# A typical module looks like this
-#
-# module M
-# def self.included(base)
-# base.send(:extend, ClassMethods)
-# base.send(:include, InstanceMethods)
-# scope :foo, :conditions => { :created_at => nil }
-# end
-#
-# module ClassMethods
-# def cm; puts 'I am a class method'; end
-# end
-#
-# module InstanceMethods
-# def im; puts 'I am an instance method'; end
-# end
-# end
-#
-# By using <tt>ActiveSupport::Concern</tt> the above module could instead be written as:
-#
-# module M
-# extend ActiveSupport::Concern
-#
-# included do
-# scope :foo, :conditions => { :created_at => nil }
-# end
-#
-# module ClassMethods
-# def cm; puts 'I am a class method'; end
-# end
-#
-# module InstanceMethods
-# def im; puts 'I am an instance method'; end
-# end
-# end
module ActiveSupport
+ # A typical module looks like this:
+ #
+ # 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:
+ #
+ # require 'active_support/concern'
+ #
+ # module M
+ # extend ActiveSupport::Concern
+ #
+ # included do
+ # scope :disabled, where(:disabled => true)
+ # end
+ #
+ # module ClassMethods
+ # ...
+ # end
+ #
+ # module InstanceMethods
+ # ...
+ # end
+ # end
+ #
+ # Moreover, it gracefully handles module dependencies. Given a +Foo+ module and a +Bar+
+ # module which depends on the former, we would typically write the following:
+ #
+ # module Foo
+ # def self.included(base)
+ # base.class_eval do
+ # def self.method_injected_by_foo
+ # ...
+ # end
+ # end
+ # end
+ # end
+ #
+ # module Bar
+ # def self.included(base)
+ # base.method_injected_by_foo
+ # end
+ # end
+ #
+ # class Host
+ # include Foo # We need to include this dependency for Bar
+ # include Bar # Bar is the module that Host really needs
+ # end
+ #
+ # But why should +Host+ care about +Bar+'s dependencies, namely +Foo+? We could try to hide
+ # these from +Host+ directly including +Foo+ in +Bar+:
+ #
+ # module Bar
+ # include Foo
+ # def self.included(base)
+ # base.method_injected_by_foo
+ # end
+ # end
+ #
+ # class Host
+ # include Bar
+ # end
+ #
+ # Unfortunately this won't work, since when +Foo+ is included, its <tt>base</tt> is the +Bar+ module,
+ # not the +Host+ class. With <tt>ActiveSupport::Concern</tt>, module dependencies are properly resolved:
+ #
+ # require 'active_support/concern'
+ #
+ # module Foo
+ # extend ActiveSupport::Concern
+ # included do
+ # class_eval do
+ # def self.method_injected_by_foo
+ # ...
+ # end
+ # end
+ # end
+ # end
+ #
+ # module Bar
+ # extend ActiveSupport::Concern
+ # include Foo
+ #
+ # included do
+ # self.method_injected_by_foo
+ # end
+ # end
+ #
+ # class Host
+ # include Bar # works, Bar takes care now of its dependencies
+ # end
+ #
module Concern
def self.extended(base)
base.instance_variable_set("@_dependencies", [])
diff --git a/activesupport/lib/active_support/configurable.rb b/activesupport/lib/active_support/configurable.rb
index f562e17c75..8c56a21ef7 100644
--- a/activesupport/lib/active_support/configurable.rb
+++ b/activesupport/lib/active_support/configurable.rb
@@ -4,18 +4,52 @@ require 'active_support/core_ext/kernel/singleton_class'
require 'active_support/core_ext/module/delegation'
module ActiveSupport
+ # Configurable provides a <tt>config</tt> method to store and retrieve
+ # configuration options as an <tt>OrderedHash</tt>.
module Configurable
extend ActiveSupport::Concern
+ class Configuration < ActiveSupport::InheritableOptions
+ def compile_methods!
+ self.class.compile_methods!(keys.reject {|key| respond_to?(key)})
+ end
+
+ # compiles reader methods so we don't have to go through method_missing
+ def self.compile_methods!(keys)
+ keys.each do |key|
+ class_eval <<-RUBY, __FILE__, __LINE__ + 1
+ def #{key}; _get(#{key.inspect}); end
+ RUBY
+ end
+ end
+ end
+
module ClassMethods
def config
- @config ||= ActiveSupport::InheritableOptions.new(superclass.respond_to?(:config) ? superclass.config : {})
+ @_config ||= if respond_to?(:superclass) && superclass.respond_to?(:config)
+ superclass.config.inheritable_copy
+ else
+ # create a new "anonymous" class that will host the compiled reader methods
+ Class.new(Configuration).new
+ end
end
def configure
yield config
end
+ # Allows you to add shortcut so that you don't have to refer to attribute through config.
+ # Also look at the example for config to contrast.
+ #
+ # class User
+ # include ActiveSupport::Configurable
+ # config_accessor :allowed_access
+ # end
+ #
+ # user = User.new
+ # user.allowed_access = true
+ # user.allowed_access # => true
+ #
def config_accessor(*names)
names.each do |name|
code, line = <<-RUBY, __LINE__ + 1
@@ -29,8 +63,25 @@ module ActiveSupport
end
end
+ # Reads and writes attributes from a configuration <tt>OrderedHash</tt>.
+ #
+ # require 'active_support/configurable'
+ #
+ # class User
+ # include ActiveSupport::Configurable
+ # end
+ #
+ # user = User.new
+ #
+ # user.config.allowed_access = true
+ # user.config.level = 1
+ #
+ # user.config.allowed_access # => true
+ # user.config.level # => 1
+ #
def config
- @config ||= ActiveSupport::InheritableOptions.new(self.class.config)
+ @_config ||= self.class.config.inheritable_copy
end
end
-end \ No newline at end of file
+end
+
diff --git a/activesupport/lib/active_support/core_ext/array/conversions.rb b/activesupport/lib/active_support/core_ext/array/conversions.rb
index 7585137aca..baefa9cae4 100644
--- a/activesupport/lib/active_support/core_ext/array/conversions.rb
+++ b/activesupport/lib/active_support/core_ext/array/conversions.rb
@@ -26,7 +26,7 @@ class Array
when 0
""
when 1
- self[0].to_s
+ self[0].to_s.dup
when 2
"#{self[0]}#{options[:two_words_connector]}#{self[1]}"
else
diff --git a/activesupport/lib/active_support/core_ext/array/random_access.rb b/activesupport/lib/active_support/core_ext/array/random_access.rb
index edac7278bc..ab1fa7cd5b 100644
--- a/activesupport/lib/active_support/core_ext/array/random_access.rb
+++ b/activesupport/lib/active_support/core_ext/array/random_access.rb
@@ -1,10 +1,10 @@
class Array
- # Backport of Array#sample based on Marc-Andre Lafortune's http://github.com/marcandre/backports/
- # Returns a random element or +n+ random elements from the 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, 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 # => 4
+ # [1,2,3,4,5,6].sample(3) # => [2, 4, 5]
# [].sample # => nil
# [].sample(3) # => []
def sample(n=nil)
@@ -24,4 +24,4 @@ class Array
result[n..size] = []
result
end unless method_defined? :sample
-end \ No newline at end of file
+end
diff --git a/activesupport/lib/active_support/core_ext/array/wrap.rb b/activesupport/lib/active_support/core_ext/array/wrap.rb
index 06b2acd662..7fabae3138 100644
--- a/activesupport/lib/active_support/core_ext/array/wrap.rb
+++ b/activesupport/lib/active_support/core_ext/array/wrap.rb
@@ -10,7 +10,7 @@ class Array
# Array.wrap(nil) # => []
# Array.wrap([1, 2, 3]) # => [1, 2, 3]
# Array.wrap(0) # => [0]
- #
+ #
# This method is similar in purpose to <tt>Kernel#Array</tt>, but there are some differences:
#
# * If the argument responds to +to_ary+ the method is invoked. <tt>Kernel#Array</tt>
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 f7f03f4d95..080604147d 100644
--- a/activesupport/lib/active_support/core_ext/big_decimal/conversions.rb
+++ b/activesupport/lib/active_support/core_ext/big_decimal/conversions.rb
@@ -1,23 +1,38 @@
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' }
- yaml_as YAML_TAG
-
# 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
+ end
+
DEFAULT_STRING_FORMAT = 'F'
def to_formatted_s(format = DEFAULT_STRING_FORMAT)
_original_to_s(format)
diff --git a/activesupport/lib/active_support/core_ext/cgi.rb b/activesupport/lib/active_support/core_ext/cgi.rb
deleted file mode 100644
index 7279a3d4da..0000000000
--- a/activesupport/lib/active_support/core_ext/cgi.rb
+++ /dev/null
@@ -1 +0,0 @@
-require 'active_support/core_ext/cgi/escape_skipping_slashes'
diff --git a/activesupport/lib/active_support/core_ext/cgi/escape_skipping_slashes.rb b/activesupport/lib/active_support/core_ext/cgi/escape_skipping_slashes.rb
deleted file mode 100644
index d3c3575748..0000000000
--- a/activesupport/lib/active_support/core_ext/cgi/escape_skipping_slashes.rb
+++ /dev/null
@@ -1,19 +0,0 @@
-require 'cgi'
-
-class CGI #:nodoc:
- if RUBY_VERSION >= '1.9'
- def self.escape_skipping_slashes(str)
- str = str.join('/') if str.respond_to? :join
- str.gsub(/([^ \/a-zA-Z0-9_.-])/n) do
- "%#{$1.unpack('H2' * $1.bytesize).join('%').upcase}"
- end.tr(' ', '+')
- end
- else
- def self.escape_skipping_slashes(str)
- str = str.join('/') if str.respond_to? :join
- str.gsub(/([^ \/a-zA-Z0-9_.-])/n) do
- "%#{$1.unpack('H2').first.upcase}"
- end.tr(' ', '+')
- end
- end
-end
diff --git a/activesupport/lib/active_support/core_ext/class/attribute.rb b/activesupport/lib/active_support/core_ext/class/attribute.rb
index bfa57fe1f7..5414b3a18f 100644
--- a/activesupport/lib/active_support/core_ext/class/attribute.rb
+++ b/activesupport/lib/active_support/core_ext/class/attribute.rb
@@ -29,19 +29,19 @@ class Class
# In such cases, you don't want to do changes in places but use setters:
#
# Base.setting = []
- # Base.setting #=> []
- # Subclass.setting #=> []
+ # Base.setting # => []
+ # Subclass.setting # => []
#
# # Appending in child changes both parent and child because it is the same object:
# Subclass.setting << :foo
- # Base.setting #=> [:foo]
- # Subclass.setting #=> [:foo]
+ # Base.setting # => [:foo]
+ # Subclass.setting # => [:foo]
#
# # Use setters to not propagate changes:
# Base.setting = []
# Subclass.setting += [:foo]
- # Base.setting #=> []
- # Subclass.setting #=> [:foo]
+ # Base.setting # => []
+ # Subclass.setting # => [:foo]
#
# For convenience, a query method is defined as well:
#
@@ -72,10 +72,21 @@ class Class
remove_possible_method(:#{name})
define_method(:#{name}) { val }
end
+
+ if singleton_class?
+ class_eval do
+ remove_possible_method(:#{name})
+ def #{name}
+ defined?(@#{name}) ? @#{name} : singleton_class.#{name}
+ end
+ end
+ end
+ val
end
+ remove_method :#{name} if method_defined?(:#{name})
def #{name}
- defined?(@#{name}) ? @#{name} : singleton_class.#{name}
+ defined?(@#{name}) ? @#{name} : self.class.#{name}
end
def #{name}?
@@ -86,4 +97,15 @@ class Class
attr_writer name if instance_writer
end
end
+
+ private
+ def singleton_class?
+ # in case somebody is crazy enough to overwrite allocate
+ allocate = Class.instance_method(:allocate)
+ # object.class always points to a real (non-singleton) class
+ allocate.bind(self).call.class != self
+ rescue TypeError
+ # MRI/YARV/JRuby all disallow creating new instances of a singleton class
+ true
+ 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 4e35b1b488..a903735acf 100644
--- a/activesupport/lib/active_support/core_ext/class/attribute_accessors.rb
+++ b/activesupport/lib/active_support/core_ext/class/attribute_accessors.rb
@@ -12,8 +12,8 @@ require 'active_support/core_ext/array/extract_options'
# end
#
# Person.hair_colors = [:brown, :black, :blonde, :red]
-# Person.hair_colors #=> [:brown, :black, :blonde, :red]
-# Person.new.hair_colors #=> [:brown, :black, :blonde, :red]
+# Person.hair_colors # => [:brown, :black, :blonde, :red]
+# Person.new.hair_colors # => [:brown, :black, :blonde, :red]
#
# To opt out of the instance writer method, pass :instance_writer => false.
# To opt out of the instance reader method, pass :instance_reader => false.
diff --git a/activesupport/lib/active_support/core_ext/class/inheritable_attributes.rb b/activesupport/lib/active_support/core_ext/class/inheritable_attributes.rb
index e844cf50d1..ca3db2349e 100644
--- a/activesupport/lib/active_support/core_ext/class/inheritable_attributes.rb
+++ b/activesupport/lib/active_support/core_ext/class/inheritable_attributes.rb
@@ -1,13 +1,15 @@
require 'active_support/core_ext/object/duplicable'
require 'active_support/core_ext/array/extract_options'
+require 'active_support/deprecation'
# Retained for backward compatibility. Methods are now included in Class.
module ClassInheritableAttributes # :nodoc:
+ DEPRECATION_WARNING_MESSAGE = "class_inheritable_attribute is deprecated, please use class_attribute method instead. Notice their behavior are slightly different, so refer to class_attribute documentation first"
end
-# It is recommend to use <tt>class_attribute</tt> over methods defined in this file. Please
+# It is recommended to use <tt>class_attribute</tt> over methods defined in this file. Please
# refer to documentation for <tt>class_attribute</tt> for more information. Officially it is not
-# deprected but <tt>class_attribute</tt> is faster.
+# deprecated but <tt>class_attribute</tt> is faster.
#
# Allows attributes to be shared within an inheritance hierarchy. Each descendant gets a copy of
# their parents' attributes, instead of just a pointer to the same. This means that the child can add elements
@@ -22,8 +24,8 @@ end
# end
#
# Person.hair_colors = [:brown, :black, :blonde, :red]
-# Person.hair_colors #=> [:brown, :black, :blonde, :red]
-# Person.new.hair_colors #=> [:brown, :black, :blonde, :red]
+# Person.hair_colors # => [:brown, :black, :blonde, :red]
+# Person.new.hair_colors # => [:brown, :black, :blonde, :red]
#
# To opt out of the instance writer method, pass :instance_writer => false.
# To opt out of the instance reader method, pass :instance_reader => false.
@@ -36,6 +38,7 @@ end
# Person.new.hair_colors # => NoMethodError
class Class # :nodoc:
def class_inheritable_reader(*syms)
+ ActiveSupport::Deprecation.warn ClassInheritableAttributes::DEPRECATION_WARNING_MESSAGE
options = syms.extract_options!
syms.each do |sym|
next if sym.is_a?(Hash)
@@ -54,6 +57,7 @@ class Class # :nodoc:
end
def class_inheritable_writer(*syms)
+ ActiveSupport::Deprecation.warn ClassInheritableAttributes::DEPRECATION_WARNING_MESSAGE
options = syms.extract_options!
syms.each do |sym|
class_eval(<<-EOS, __FILE__, __LINE__ + 1)
@@ -71,6 +75,7 @@ class Class # :nodoc:
end
def class_inheritable_array_writer(*syms)
+ ActiveSupport::Deprecation.warn ClassInheritableAttributes::DEPRECATION_WARNING_MESSAGE
options = syms.extract_options!
syms.each do |sym|
class_eval(<<-EOS, __FILE__, __LINE__ + 1)
@@ -88,6 +93,7 @@ class Class # :nodoc:
end
def class_inheritable_hash_writer(*syms)
+ ActiveSupport::Deprecation.warn ClassInheritableAttributes::DEPRECATION_WARNING_MESSAGE
options = syms.extract_options!
syms.each do |sym|
class_eval(<<-EOS, __FILE__, __LINE__ + 1)
@@ -124,6 +130,7 @@ class Class # :nodoc:
end
def write_inheritable_attribute(key, value)
+ ActiveSupport::Deprecation.warn ClassInheritableAttributes::DEPRECATION_WARNING_MESSAGE
if inheritable_attributes.equal?(EMPTY_INHERITABLE_ATTRIBUTES)
@inheritable_attributes = {}
end
@@ -141,10 +148,12 @@ class Class # :nodoc:
end
def read_inheritable_attribute(key)
+ ActiveSupport::Deprecation.warn ClassInheritableAttributes::DEPRECATION_WARNING_MESSAGE
inheritable_attributes[key]
end
def reset_inheritable_attributes
+ ActiveSupport::Deprecation.warn ClassInheritableAttributes::DEPRECATION_WARNING_MESSAGE
@inheritable_attributes = EMPTY_INHERITABLE_ATTRIBUTES
end
@@ -158,9 +167,9 @@ class Class # :nodoc:
if inheritable_attributes.equal?(EMPTY_INHERITABLE_ATTRIBUTES)
new_inheritable_attributes = EMPTY_INHERITABLE_ATTRIBUTES
else
- new_inheritable_attributes = inheritable_attributes.inject({}) do |memo, (key, value)|
- memo.update(key => value.duplicable? ? value.dup : value)
- end
+ new_inheritable_attributes = Hash[inheritable_attributes.map do |(key, value)|
+ [key, value.duplicable? ? value.dup : value]
+ end]
end
child.instance_variable_set('@inheritable_attributes', new_inheritable_attributes)
@@ -169,86 +178,3 @@ class Class # :nodoc:
alias inherited_without_inheritable_attributes inherited
alias inherited inherited_with_inheritable_attributes
end
-
-class Class
- # Defines class-level inheritable attribute reader. Attributes are available to subclasses,
- # each subclass has a copy of parent's attribute.
- #
- # @param *syms<Array[#to_s]> Array of attributes to define inheritable reader for.
- # @return <Array[#to_s]> Array of attributes converted into inheritable_readers.
- #
- # @api public
- #
- # @todo Do we want to block instance_reader via :instance_reader => false
- # @todo It would be preferable that we do something with a Hash passed in
- # (error out or do the same as other methods above) instead of silently
- # moving on). In particular, this makes the return value of this function
- # less useful.
- def extlib_inheritable_reader(*ivars, &block)
- options = ivars.extract_options!
-
- ivars.each do |ivar|
- self.class_eval <<-RUBY, __FILE__, __LINE__ + 1
- def self.#{ivar}
- return @#{ivar} if self.object_id == #{self.object_id} || defined?(@#{ivar})
- ivar = superclass.#{ivar}
- return nil if ivar.nil? && !#{self}.instance_variable_defined?("@#{ivar}")
- @#{ivar} = ivar.duplicable? ? ivar.dup : ivar
- end
- RUBY
- unless options[:instance_reader] == false
- self.class_eval <<-RUBY, __FILE__, __LINE__ + 1
- def #{ivar}
- self.class.#{ivar}
- end
- RUBY
- end
- instance_variable_set(:"@#{ivar}", yield) if block_given?
- end
- end
-
- # Defines class-level inheritable attribute writer. Attributes are available to subclasses,
- # each subclass has a copy of parent's attribute.
- #
- # @param *syms<Array[*#to_s, Hash{:instance_writer => Boolean}]> Array of attributes to
- # define inheritable writer for.
- # @option syms :instance_writer<Boolean> if true, instance-level inheritable attribute writer is defined.
- # @return <Array[#to_s]> An Array of the attributes that were made into inheritable writers.
- #
- # @api public
- #
- # @todo We need a style for class_eval <<-HEREDOC. I'd like to make it
- # class_eval(<<-RUBY, __FILE__, __LINE__), but we should codify it somewhere.
- def extlib_inheritable_writer(*ivars)
- options = ivars.extract_options!
-
- ivars.each do |ivar|
- self.class_eval <<-RUBY, __FILE__, __LINE__ + 1
- def self.#{ivar}=(obj)
- @#{ivar} = obj
- end
- RUBY
- unless options[:instance_writer] == false
- self.class_eval <<-RUBY, __FILE__, __LINE__ + 1
- def #{ivar}=(obj) self.class.#{ivar} = obj end
- RUBY
- end
-
- self.send("#{ivar}=", yield) if block_given?
- end
- end
-
- # Defines class-level inheritable attribute accessor. Attributes are available to subclasses,
- # each subclass has a copy of parent's attribute.
- #
- # @param *syms<Array[*#to_s, Hash{:instance_writer => Boolean}]> Array of attributes to
- # define inheritable accessor for.
- # @option syms :instance_writer<Boolean> if true, instance-level inheritable attribute writer is defined.
- # @return <Array[#to_s]> An Array of attributes turned into inheritable accessors.
- #
- # @api public
- def extlib_inheritable_accessor(*syms, &block)
- extlib_inheritable_reader(*syms)
- extlib_inheritable_writer(*syms, &block)
- 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 3e5d1a2a42..46e9daaa8f 100644
--- a/activesupport/lib/active_support/core_ext/class/subclasses.rb
+++ b/activesupport/lib/active_support/core_ext/class/subclasses.rb
@@ -2,49 +2,35 @@ require 'active_support/core_ext/module/anonymous'
require 'active_support/core_ext/module/reachable'
class Class #:nodoc:
- # Rubinius
- if defined?(Class.__subclasses__)
- alias :subclasses :__subclasses__
+ begin
+ ObjectSpace.each_object(Class.new) {}
def descendants
descendants = []
- __subclasses__.each do |k|
- descendants << k
- descendants.concat k.descendants
+ ObjectSpace.each_object(class << self; self; end) do |k|
+ descendants.unshift k unless k == self
end
descendants
end
- else # MRI
- begin
- ObjectSpace.each_object(Class.new) {}
-
- def descendants
- descendants = []
- ObjectSpace.each_object(class << self; self; end) do |k|
- descendants.unshift k unless k == self
- end
- descendants
- end
- rescue StandardError # JRuby
- def descendants
- descendants = []
- ObjectSpace.each_object(Class) do |k|
- descendants.unshift k if k < self
- end
- descendants.uniq!
- descendants
+ rescue StandardError # JRuby
+ def descendants
+ descendants = []
+ ObjectSpace.each_object(Class) do |k|
+ descendants.unshift k if k < self
end
+ descendants.uniq!
+ descendants
end
+ end
- # Returns an array with the direct children of +self+.
- #
- # Integer.subclasses # => [Bignum, Fixnum]
- def subclasses
- subclasses, chain = [], descendants
- chain.each do |k|
- subclasses << k unless chain.any? { |c| c > k }
- end
- subclasses
+ # Returns an array with the direct children of +self+.
+ #
+ # Integer.subclasses # => [Bignum, Fixnum]
+ def subclasses
+ subclasses, chain = [], descendants
+ chain.each do |k|
+ subclasses << k unless chain.any? { |c| c > k }
end
+ subclasses
end
end
diff --git a/activesupport/lib/active_support/core_ext/date/calculations.rb b/activesupport/lib/active_support/core_ext/date/calculations.rb
index c5b54318ce..724e076407 100644
--- a/activesupport/lib/active_support/core_ext/date/calculations.rb
+++ b/activesupport/lib/active_support/core_ext/date/calculations.rb
@@ -5,9 +5,11 @@ 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.
@@ -34,9 +36,9 @@ class Date
::Date.current.tomorrow
end
- # Returns Time.zone.today when config.time_zone is set, otherwise just returns Date.today.
+ # Returns Time.zone.today when <tt>Time.zone</tt> or <tt>config.time_zone</tt> are set, otherwise just returns Date.today.
def current
- ::Time.zone_default ? ::Time.zone.today : ::Date.today
+ ::Time.zone ? ::Time.zone.today : ::Date.today
end
end
@@ -127,6 +129,11 @@ class Date
)
end
+ # Returns a new Date/DateTime representing the time a number of specified weeks ago.
+ def weeks_ago(weeks)
+ advance(:weeks => -weeks)
+ end
+
# Returns a new Date/DateTime representing the time a number of specified months ago.
def months_ago(months)
advance(:months => -months)
@@ -156,7 +163,7 @@ class Date
def next_year
years_since(1)
end unless method_defined?(:next_year)
-
+
# Shorthand for months_ago(1)
def prev_month
months_ago(1)
@@ -185,10 +192,15 @@ class Date
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).
+ def prev_week(day = :monday)
+ result = (self - 7).beginning_of_week + DAYS_INTO_WEEK[day]
+ self.acts_like?(:time) ? result.change(:hour => 0) : result
+ end
+
# Returns a new Date/DateTime representing the start of the given day in next week (default is Monday).
def next_week(day = :monday)
- days_into_week = { :monday => 0, :tuesday => 1, :wednesday => 2, :thursday => 3, :friday => 4, :saturday => 5, :sunday => 6}
- result = (self + 7).beginning_of_week + days_into_week[day]
+ result = (self + 7).beginning_of_week + DAYS_INTO_WEEK[day]
self.acts_like?(:time) ? result.change(:hour => 0) : result
end
diff --git a/activesupport/lib/active_support/core_ext/date/conversions.rb b/activesupport/lib/active_support/core_ext/date/conversions.rb
index 092f936961..769ead9544 100644
--- a/activesupport/lib/active_support/core_ext/date/conversions.rb
+++ b/activesupport/lib/active_support/core_ext/date/conversions.rb
@@ -1,5 +1,5 @@
require 'date'
-require 'active_support/inflector'
+require 'active_support/inflector/methods'
require 'active_support/core_ext/date/zones'
class Date
@@ -93,6 +93,12 @@ class Date
::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/zones.rb b/activesupport/lib/active_support/core_ext/date/zones.rb
index 3a83af6be2..a70b47b7bc 100644
--- a/activesupport/lib/active_support/core_ext/date/zones.rb
+++ b/activesupport/lib/active_support/core_ext/date/zones.rb
@@ -2,10 +2,10 @@ require 'date'
require 'active_support/core_ext/time/zones'
class Date
- # Converts Date to a TimeWithZone in the current zone if Time.zone_default is set,
- # otherwise converts Date to a Time via Date#to_time
+ # Converts Date to a TimeWithZone in the current zone if Time.zone or Time.zone_default
+ # is set, otherwise converts Date to a Time via Date#to_time
def to_time_in_current_zone
- if ::Time.zone_default
+ if ::Time.zone
::Time.zone.local(year, month, day)
else
to_time
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 1dc3933e12..48cf1a435d 100644
--- a/activesupport/lib/active_support/core_ext/date_time/calculations.rb
+++ b/activesupport/lib/active_support/core_ext/date_time/calculations.rb
@@ -1,6 +1,4 @@
require 'rational' unless RUBY_VERSION >= '1.9.2'
-require 'active_support/core_ext/object/acts_like'
-require 'active_support/core_ext/time/zones'
class DateTime
class << self
@@ -9,8 +7,9 @@ class DateTime
::Time.local(2007).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>.
def current
- ::Time.zone_default ? ::Time.zone.now.to_datetime : ::Time.now.to_datetime
+ ::Time.zone ? ::Time.zone.now.to_datetime : ::Time.now.to_datetime
end
end
@@ -82,6 +81,29 @@ 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:
@@ -104,11 +126,7 @@ class DateTime
end
# Layers additional behavior on DateTime#<=> so that Time and ActiveSupport::TimeWithZone instances can be compared with a DateTime
- def compare_with_coercion(other)
- other = other.comparable_time if other.respond_to?(:comparable_time)
- other = other.to_datetime unless other.acts_like?(:date)
- compare_without_coercion(other)
+ def <=>(other)
+ super other.to_datetime
end
- alias_method :compare_without_coercion, :<=>
- alias_method :<=>, :compare_with_coercion
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 47b8aa59fb..21b84b994b 100644
--- a/activesupport/lib/active_support/core_ext/date_time/conversions.rb
+++ b/activesupport/lib/active_support/core_ext/date_time/conversions.rb
@@ -1,4 +1,4 @@
-require 'active_support/inflector'
+require 'active_support/inflector/methods'
require 'active_support/core_ext/time/conversions'
require 'active_support/core_ext/date_time/calculations'
require 'active_support/values/time_zone'
@@ -9,12 +9,12 @@ class DateTime
remove_method :to_time if instance_methods.include?(:to_time)
# Convert to a formatted string. See Time::DATE_FORMATS for predefined formats.
- #
+ #
# This method is aliased to <tt>to_s</tt>.
- #
+ #
# === Examples
# datetime = DateTime.civil(2007, 12, 4, 0, 0, 0, 0) # => Tue, 04 Dec 2007 00:00:00 +0000
- #
+ #
# datetime.to_formatted_s(:db) # => "2007-12-04 00:00:00"
# datetime.to_s(:db) # => "2007-12-04 00:00:00"
# datetime.to_s(:number) # => "20071204000000"
@@ -50,7 +50,7 @@ class DateTime
def formatted_offset(colon = true, alternate_utc_string = nil)
utc? && alternate_utc_string || ActiveSupport::TimeZone.seconds_to_utc_offset(utc_offset, colon)
end
-
+
# Overrides the default inspect method with a human readable one, e.g., "Mon, 21 Feb 2005 14:30:00 +0000"
def readable_inspect
to_s(:rfc822)
@@ -66,7 +66,7 @@ class DateTime
# 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) : self
+ self.offset == 0 ? ::Time.utc_time(year, month, day, hour, min, sec, sec_fraction * (RUBY_VERSION < '1.9' ? 86400000000 : 1000000)) : self
end
# To be able to keep Times, Dates and DateTimes interchangeable on conversions
@@ -83,8 +83,8 @@ class DateTime
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
+
+ # Converts self to a floating-point number of seconds since the Unix epoch
def to_f
seconds_since_unix_epoch.to_f
end
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 a7411d54ae..82a4f7ac5a 100644
--- a/activesupport/lib/active_support/core_ext/date_time/zones.rb
+++ b/activesupport/lib/active_support/core_ext/date_time/zones.rb
@@ -9,7 +9,7 @@ class DateTime
# This method is similar to Time#localtime, except that it uses <tt>Time.zone</tt> as the local zone
# instead of the operating system's time zone.
#
- # You can also pass in a TimeZone instance or string that identifies a TimeZone as an argument,
+ # You can also pass in a TimeZone instance or string that identifies a TimeZone as an argument,
# and the conversion will be based on that zone instead of <tt>Time.zone</tt>.
#
# DateTime.new(2000).in_time_zone('Alaska') # => Fri, 31 Dec 1999 15:00:00 AKST -09:00
diff --git a/activesupport/lib/active_support/core_ext/enumerable.rb b/activesupport/lib/active_support/core_ext/enumerable.rb
index f76ed401cd..6d7f771b5d 100644
--- a/activesupport/lib/active_support/core_ext/enumerable.rb
+++ b/activesupport/lib/active_support/core_ext/enumerable.rb
@@ -9,7 +9,7 @@ module Enumerable
#
# Example:
#
- # latest_transcripts.group_by(&:day).each do |day, transcripts|
+ # latest_transcripts.group_by(&:day).each do |day, transcripts|
# p "#{day} -> #{transcripts.map(&:class).join(', ')}"
# end
# "2006-03-01 -> Transcript"
@@ -88,21 +88,18 @@ module Enumerable
# => { "nextangle" => <Person ...>, "chade-" => <Person ...>, ...}
# people.index_by { |person| "#{person.first_name} #{person.last_name}" }
# => { "Chade- Fowlersburg-e" => <Person ...>, "David Heinemeier Hansson" => <Person ...>, ...}
- #
+ #
def index_by
- inject({}) do |accum, elem|
- accum[yield(elem)] = elem
- accum
- end
+ Hash[map { |elem| [yield(elem), elem] }]
end
-
+
# Returns true if the collection has more than 1 element. Functionally equivalent to collection.size > 1.
- # Works with a block too ala 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 people.many? { |p| p.age > 26 } returns true if more than 1 person is over 26.
def many?(&block)
size = block_given? ? select(&block).size : self.size
size > 1
end
-
+
# The negative of the Enumerable#include?. Returns true if the collection does not include the object.
def exclude?(object)
!include?(object)
diff --git a/activesupport/lib/active_support/core_ext/float/rounding.rb b/activesupport/lib/active_support/core_ext/float/rounding.rb
index 9bdf5bba7b..0d4fb87665 100644
--- a/activesupport/lib/active_support/core_ext/float/rounding.rb
+++ b/activesupport/lib/active_support/core_ext/float/rounding.rb
@@ -16,4 +16,4 @@ class Float
precisionless_round
end
end
-end
+end if RUBY_VERSION < '1.9'
diff --git a/activesupport/lib/active_support/core_ext/hash.rb b/activesupport/lib/active_support/core_ext/hash.rb
index 501483498d..fd1cda991e 100644
--- a/activesupport/lib/active_support/core_ext/hash.rb
+++ b/activesupport/lib/active_support/core_ext/hash.rb
@@ -1,5 +1,6 @@
require 'active_support/core_ext/hash/conversions'
require 'active_support/core_ext/hash/deep_merge'
+require 'active_support/core_ext/hash/deep_dup'
require 'active_support/core_ext/hash/diff'
require 'active_support/core_ext/hash/except'
require 'active_support/core_ext/hash/indifferent_access'
diff --git a/activesupport/lib/active_support/core_ext/hash/conversions.rb b/activesupport/lib/active_support/core_ext/hash/conversions.rb
index 2763af6121..3005fef44c 100644
--- a/activesupport/lib/active_support/core_ext/hash/conversions.rb
+++ b/activesupport/lib/active_support/core_ext/hash/conversions.rb
@@ -7,7 +7,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
# # =>
# # <?xml version="1.0" encoding="UTF-8"?>
@@ -15,22 +15,34 @@ class Hash
# # <foo type="integer">1</foo>
# # <bar type="integer">2</bar>
# # </hash>
- #
+ #
# To do so, the method loops over the pairs and builds nodes that depend on
# the _values_. Given a pair +key+, +value+:
- #
+ #
# * If +value+ is a hash there's a recursive call with +key+ as <tt>:root</tt>.
- #
+ #
# * If +value+ is an array there's a recursive call with +key+ as <tt>:root</tt>,
# and +key+ singularized as <tt>:children</tt>.
- #
+ #
# * 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. Its
- # return value becomes a new node.
- #
+ # 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) })
+ # # => "<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!"
+ # end
+ # end
+ #
+ # {:foo => Foo.new}.to_xml(:skip_instruct => true)
+ # # => "<hash><bar>fooing!</bar></hash>"
+ #
# * Otherwise, a node with +key+ as tag is created with a string representation of
# +value+ as text node. If +value+ is +nil+ an attribute "nil" set to "true" is added.
# Unless the option <tt>:skip_types</tt> exists and is true, an attribute "type" is
@@ -48,9 +60,9 @@ class Hash
# "DateTime" => "datetime",
# "Time" => "datetime"
# }
- #
+ #
# By default the root node is "hash", but that's configurable via the <tt>:root</tt> option.
- #
+ #
# The default XML builder is a fresh instance of <tt>Builder::XmlMarkup</tt>. You can
# configure your own builder with the <tt>:builder</tt> option. The method also accepts
# options like <tt>:dasherize</tt> and friends, they are forwarded to the builder.
@@ -108,17 +120,14 @@ class Hash
# blank or nil parsed values are represented by nil
elsif value.blank? || value['nil'] == 'true'
nil
- # If the type is the only element which makes it then
+ # If the type is the only element which makes it then
# this still makes the value nil, except if type is
# a XML node(where type['value'] is a Hash)
elsif value['type'] && value.size == 1 && !value['type'].is_a?(::Hash)
nil
else
- xml_value = value.inject({}) do |h,(k,v)|
- h[k] = typecast_xml_value(v)
- h
- end
-
+ xml_value = Hash[value.map { |k,v| [k, typecast_xml_value(v)] }]
+
# 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
@@ -136,10 +145,7 @@ class Hash
def unrename_keys(params)
case params.class.to_s
when "Hash"
- params.inject({}) do |h,(k,v)|
- h[k.to_s.tr("-", "_")] = unrename_keys(v)
- h
- end
+ Hash[params.map { |k,v| [k.to_s.tr("-", "_"), unrename_keys(v)] } ]
when "Array"
params.map { |v| unrename_keys(v) }
else
diff --git a/activesupport/lib/active_support/core_ext/hash/deep_dup.rb b/activesupport/lib/active_support/core_ext/hash/deep_dup.rb
new file mode 100644
index 0000000000..447142605c
--- /dev/null
+++ b/activesupport/lib/active_support/core_ext/hash/deep_dup.rb
@@ -0,0 +1,11 @@
+class Hash
+ # Returns a deep copy of 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
+ end
+ duplicate
+ end
+end
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 0420e206af..aad4b61e16 100644
--- a/activesupport/lib/active_support/core_ext/hash/indifferent_access.rb
+++ b/activesupport/lib/active_support/core_ext/hash/indifferent_access.rb
@@ -7,8 +7,6 @@ class Hash
# {:a => 1}.with_indifferent_access["a"] # => 1
#
def with_indifferent_access
- hash = ActiveSupport::HashWithIndifferentAccess.new(self)
- hash.default = self.default
- hash
+ ActiveSupport::HashWithIndifferentAccess.new_from_hash_copying_default(self)
end
end
diff --git a/activesupport/lib/active_support/core_ext/hash/keys.rb b/activesupport/lib/active_support/core_ext/hash/keys.rb
index 045a6944fa..d8748b1138 100644
--- a/activesupport/lib/active_support/core_ext/hash/keys.rb
+++ b/activesupport/lib/active_support/core_ext/hash/keys.rb
@@ -35,11 +35,13 @@ class Hash
# as keys, this will fail.
#
# ==== Examples
- # { :name => "Rob", :years => "28" }.assert_valid_keys(:name, :age) # => raises "ArgumentError: Unknown key(s): years"
- # { :name => "Rob", :age => "28" }.assert_valid_keys("name", "age") # => raises "ArgumentError: Unknown key(s): name, age"
+ # { :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)
- unknown_keys = keys - [valid_keys].flatten
- raise(ArgumentError, "Unknown key(s): #{unknown_keys.join(", ")}") unless unknown_keys.empty?
+ valid_keys.flatten!
+ each_key do |k|
+ raise(ArgumentError, "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 d7ebd5feef..01863a162b 100644
--- a/activesupport/lib/active_support/core_ext/hash/reverse_merge.rb
+++ b/activesupport/lib/active_support/core_ext/hash/reverse_merge.rb
@@ -1,27 +1,22 @@
class Hash
- # Allows for reverse merging two hashes where the keys in the calling hash take precedence over those
- # in the <tt>other_hash</tt>. This is particularly useful for initializing an option hash with default values:
+ # Merges the caller into +other_hash+. For example,
#
- # def setup(options = {})
- # options.reverse_merge! :size => 25, :velocity => 10
- # end
+ # options = options.reverse_merge(:size => 25, :velocity => 10)
#
- # Using <tt>merge</tt>, the above example would look as follows:
+ # is equivalent to
#
- # def setup(options = {})
- # { :size => 25, :velocity => 10 }.merge(options)
- # end
+ # options = {:size => 25, :velocity => 10}.merge(options)
#
- # The default <tt>:size</tt> and <tt>:velocity</tt> are only set if the +options+ hash passed in doesn't already
- # have the respective key.
+ # This is particularly useful for initializing an options hash
+ # with default values.
def reverse_merge(other_hash)
other_hash.merge(self)
end
- # Performs the opposite of <tt>merge</tt>, with the keys and values from the first hash taking precedence over the second.
- # Modifies the receiver in place.
+ # Destructive +reverse_merge+.
def reverse_merge!(other_hash)
- merge!( other_hash ){|k,o,n| o }
+ # right wins if there is no left
+ merge!( other_hash ){|key,left,right| left }
end
alias_method :reverse_update, :reverse_merge!
diff --git a/activesupport/lib/active_support/core_ext/integer/time.rb b/activesupport/lib/active_support/core_ext/integer/time.rb
index a0ccf0e971..c677400396 100644
--- a/activesupport/lib/active_support/core_ext/integer/time.rb
+++ b/activesupport/lib/active_support/core_ext/integer/time.rb
@@ -1,7 +1,7 @@
class Integer
# Enables the use of time calculations and declarations, like 45.minutes + 2.hours + 4.years.
#
- # These methods use Time#advance for precise date calculations when using from_now, ago, etc.
+ # These methods use Time#advance for precise date calculations when using from_now, ago, etc.
# as well as adding or subtracting their results from a Time object. For example:
#
# # equivalent to Time.now.advance(:months => 1)
@@ -12,7 +12,7 @@ class Integer
#
# # equivalent to Time.now.advance(:months => 4, :years => 5)
# (4.months + 5.years).from_now
- #
+ #
# While these methods provide precise calculation when used as in the examples above, care
# should be taken to note that this is not true if the result of `months', `years', etc is
# converted before use:
diff --git a/activesupport/lib/active_support/core_ext/kernel/reporting.rb b/activesupport/lib/active_support/core_ext/kernel/reporting.rb
index 7c455f66d5..37a827123a 100644
--- a/activesupport/lib/active_support/core_ext/kernel/reporting.rb
+++ b/activesupport/lib/active_support/core_ext/kernel/reporting.rb
@@ -38,7 +38,7 @@ module Kernel
# puts 'But this will'
def silence_stream(stream)
old_stream = stream.dup
- stream.reopen(Config::CONFIG['host_os'] =~ /mswin|mingw/ ? 'NUL:' : '/dev/null')
+ stream.reopen(RbConfig::CONFIG['host_os'] =~ /mswin|mingw/ ? 'NUL:' : '/dev/null')
stream.sync = true
yield
ensure
@@ -59,4 +59,23 @@ module Kernel
raise unless exception_classes.any? { |cls| e.kind_of?(cls) }
end
end
+
+ # Captures the given stream and returns it:
+ #
+ # stream = capture(:stdout){ puts "Cool" }
+ # stream # => "Cool\n"
+ #
+ def capture(stream)
+ begin
+ stream = stream.to_s
+ eval "$#{stream} = StringIO.new"
+ yield
+ result = eval("$#{stream}").string
+ ensure
+ eval("$#{stream} = #{stream.upcase}")
+ end
+
+ result
+ end
+ alias :silence :capture
end
diff --git a/activesupport/lib/active_support/core_ext/logger.rb b/activesupport/lib/active_support/core_ext/logger.rb
index a1c351bfd9..e63a0a9ed9 100644
--- a/activesupport/lib/active_support/core_ext/logger.rb
+++ b/activesupport/lib/active_support/core_ext/logger.rb
@@ -4,18 +4,17 @@ require 'active_support/core_ext/class/attribute_accessors'
class Logger #:nodoc:
def self.define_around_helper(level)
module_eval <<-end_eval, __FILE__, __LINE__ + 1
- def around_#{level}(before_message, after_message, &block) # def around_debug(before_message, after_message, &block)
- self.#{level}(before_message) # self.debug(before_message)
- return_value = block.call(self) # return_value = block.call(self)
- self.#{level}(after_message) # self.debug(after_message)
- return return_value # return return_value
- end # end
+ def around_#{level}(before_message, after_message) # def around_debug(before_message, after_message, &block)
+ self.#{level}(before_message) # self.debug(before_message)
+ return_value = yield(self) # return_value = yield(self)
+ self.#{level}(after_message) # self.debug(after_message)
+ return_value # return_value
+ end # end
end_eval
end
[:debug, :info, :error, :fatal].each {|level| define_around_helper(level) }
end
-
require 'logger'
# Extensions to the built-in Ruby logger.
@@ -65,11 +64,11 @@ class Logger
formatter.datetime_format if formatter.respond_to?(:datetime_format)
end
- alias :old_formatter :formatter if method_defined?(:formatter)
- # Get the current formatter. The default formatter is a SimpleFormatter which only
- # displays the log message
- def formatter
- @formatter ||= SimpleFormatter.new
+ alias :old_initialize :initialize
+ # Overwrite initialize to set a default formatter.
+ def initialize(*args)
+ old_initialize(*args)
+ self.formatter = SimpleFormatter.new
end
# Simple formatter which only displays the message.
@@ -79,30 +78,4 @@ class Logger
"#{String === msg ? msg : msg.inspect}\n"
end
end
-
- private
- alias old_format_message format_message
-
- # Ruby 1.8.3 transposed the msg and progname arguments to format_message.
- # We can't test RUBY_VERSION because some distributions don't keep Ruby
- # and its standard library in sync, leading to installations of Ruby 1.8.2
- # with Logger from 1.8.3 and vice versa.
- if method_defined?(:formatter=)
- def format_message(severity, timestamp, progname, msg)
- formatter.call(severity, timestamp, progname, msg)
- end
- else
- def format_message(severity, timestamp, msg, progname)
- formatter.call(severity, timestamp, progname, msg)
- end
-
- attr_writer :formatter
- public :formatter=
-
- alias old_format_datetime format_datetime
- def format_datetime(datetime) datetime end
-
- alias old_msg2str msg2str
- def msg2str(msg) msg end
- 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 df25a09ec9..3982c9c586 100644
--- a/activesupport/lib/active_support/core_ext/module/anonymous.rb
+++ b/activesupport/lib/active_support/core_ext/module/anonymous.rb
@@ -19,6 +19,6 @@ class Module
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.blank?
end
end
diff --git a/activesupport/lib/active_support/core_ext/module/attr_accessor_with_default.rb b/activesupport/lib/active_support/core_ext/module/attr_accessor_with_default.rb
index 28ac89dab9..e3259a0a84 100644
--- a/activesupport/lib/active_support/core_ext/module/attr_accessor_with_default.rb
+++ b/activesupport/lib/active_support/core_ext/module/attr_accessor_with_default.rb
@@ -2,30 +2,29 @@ class Module
# Declare an attribute accessor with an initial default return value.
#
# To give attribute <tt>:age</tt> the initial value <tt>25</tt>:
- #
+ #
# class Person
# attr_accessor_with_default :age, 25
# end
#
- # some_person.age
- # => 25
- # some_person.age = 26
- # some_person.age
- # => 26
+ # person = Person.new
+ # person.age # => 25
+ #
+ # person.age = 26
+ # person.age # => 26
#
# To give attribute <tt>:element_name</tt> a dynamic default value, evaluated
# in scope of self:
#
- # attr_accessor_with_default(:element_name) { name.underscore }
+ # attr_accessor_with_default(:element_name) { name.underscore }
#
- def attr_accessor_with_default(sym, default = nil, &block)
- raise 'Default value or block required' unless !default.nil? || block
- define_method(sym, block_given? ? block : Proc.new { default })
+ def attr_accessor_with_default(sym, default = Proc.new)
+ define_method(sym, block_given? ? default : Proc.new { default })
module_eval(<<-EVAL, __FILE__, __LINE__ + 1)
- def #{sym}=(value) # def age=(value)
- class << self; attr_reader :#{sym} end # class << self; attr_reader :age end
- @#{sym} = value # @age = value
- end # end
+ def #{sym}=(value) # def age=(value)
+ class << self; attr_accessor :#{sym} end # class << self; attr_accessor :age end
+ @#{sym} = value # @age = value
+ end # end
EVAL
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 28bc30ae26..00db75bfec 100644
--- a/activesupport/lib/active_support/core_ext/module/attr_internal.rb
+++ b/activesupport/lib/active_support/core_ext/module/attr_internal.rb
@@ -1,16 +1,12 @@
class Module
# Declares an attribute reader backed by an internally-named instance variable.
def attr_internal_reader(*attrs)
- attrs.each do |attr|
- module_eval "def #{attr}() #{attr_internal_ivar_name(attr)} end", __FILE__, __LINE__
- end
+ attrs.each {|attr_name| attr_internal_define(attr_name, :reader)}
end
# Declares an attribute writer backed by an internally-named instance variable.
def attr_internal_writer(*attrs)
- attrs.each do |attr|
- module_eval "def #{attr}=(v) #{attr_internal_ivar_name(attr)} = v end", __FILE__, __LINE__
- end
+ attrs.each {|attr_name| attr_internal_define(attr_name, :writer)}
end
# Declares an attribute reader and writer backed by an internally-named instance
@@ -29,4 +25,15 @@ class Module
def attr_internal_ivar_name(attr)
Module.attr_internal_naming_format % attr
end
+
+ def attr_internal_define(attr_name, type)
+ internal_name = attr_internal_ivar_name(attr_name).sub(/\A@/, '')
+ class_eval do # class_eval is necessary on 1.9 or else the methods a made private
+ # use native attr_* methods as they are faster on some Ruby implementations
+ send("attr_#{type}", internal_name)
+ end
+ attr_name, internal_name = "#{attr_name}=", "#{internal_name}=" if type == :writer
+ alias_method attr_name, internal_name
+ remove_method internal_name
+ end
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 2d88cb57e5..871f5cef3b 100644
--- a/activesupport/lib/active_support/core_ext/module/attribute_accessors.rb
+++ b/activesupport/lib/active_support/core_ext/module/attribute_accessors.rb
@@ -11,7 +11,7 @@ class Module
@@#{sym}
end
EOS
-
+
unless options[:instance_reader] == false
class_eval(<<-EOS, __FILE__, __LINE__ + 1)
def #{sym}
diff --git a/activesupport/lib/active_support/core_ext/module/delegation.rb b/activesupport/lib/active_support/core_ext/module/delegation.rb
index 40a1866428..3a7652f5bf 100644
--- a/activesupport/lib/active_support/core_ext/module/delegation.rb
+++ b/activesupport/lib/active_support/core_ext/module/delegation.rb
@@ -113,7 +113,7 @@ class Module
raise ArgumentError, "Can only automatically set the delegation prefix when delegating to a method."
end
- prefix = options[:prefix] && "#{options[:prefix] == true ? to : options[:prefix]}_"
+ prefix = options[:prefix] && "#{options[:prefix] == true ? to : options[:prefix]}_" || ''
file, line = caller.first.split(':', 2)
line = line.to_i
diff --git a/activesupport/lib/active_support/core_ext/module/deprecation.rb b/activesupport/lib/active_support/core_ext/module/deprecation.rb
index 5a5b4e3f80..9c169a2598 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'
+
class Module
# Declare that a method has been deprecated.
# deprecate :foo
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 b8c01aca0e..07d7c9b018 100644
--- a/activesupport/lib/active_support/core_ext/module/remove_method.rb
+++ b/activesupport/lib/active_support/core_ext/module/remove_method.rb
@@ -3,7 +3,7 @@ class Module
remove_method(method)
rescue NameError
end
-
+
def redefine_method(method, &block)
remove_possible_method(method)
define_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
index de76a069d6..ed16c2f71b 100644
--- a/activesupport/lib/active_support/core_ext/module/synchronization.rb
+++ b/activesupport/lib/active_support/core_ext/module/synchronization.rb
@@ -1,11 +1,12 @@
+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
+ # 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,
+ # The :with option should be a symbol or string, and can represent a method,
# constant, or instance or class variable.
# Example:
# class SharedCache
diff --git a/activesupport/lib/active_support/core_ext/numeric/time.rb b/activesupport/lib/active_support/core_ext/numeric/time.rb
index d1062805c5..e73915ffcf 100644
--- a/activesupport/lib/active_support/core_ext/numeric/time.rb
+++ b/activesupport/lib/active_support/core_ext/numeric/time.rb
@@ -3,7 +3,7 @@ require 'active_support/duration'
class Numeric
# Enables the use of time calculations and declarations, like 45.minutes + 2.hours + 4.years.
#
- # These methods use Time#advance for precise date calculations when using from_now, ago, etc.
+ # These methods use Time#advance for precise date calculations when using from_now, ago, etc.
# as well as adding or subtracting their results from a Time object. For example:
#
# # equivalent to Time.now.advance(:months => 1)
@@ -14,7 +14,7 @@ class Numeric
#
# # equivalent to Time.now.advance(:months => 4, :years => 5)
# (4.months + 5.years).from_now
- #
+ #
# While these methods provide precise calculation when used as in the examples above, care
# should be taken to note that this is not true if the result of `months', `years', etc is
# converted before use:
@@ -25,8 +25,8 @@ class Numeric
# # equivalent to 365.25.days.to_f.from_now
# 1.year.to_f.from_now
#
- # In such cases, Ruby's core
- # Date[http://stdlib.rubyonrails.org/libdoc/date/rdoc/index.html] and
+ # 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
def seconds
@@ -37,13 +37,13 @@ class Numeric
def minutes
ActiveSupport::Duration.new(self * 60, [[:seconds, self * 60]])
end
- alias :minute :minutes
-
+ alias :minute :minutes
+
def hours
ActiveSupport::Duration.new(self * 3600, [[:seconds, self * 3600]])
end
alias :hour :hours
-
+
def days
ActiveSupport::Duration.new(self * 24.hours, [[:days, self]])
end
@@ -53,14 +53,14 @@ class Numeric
ActiveSupport::Duration.new(self * 7.days, [[:days, self * 7]])
end
alias :week :weeks
-
+
def fortnights
ActiveSupport::Duration.new(self * 2.weeks, [[:days, self * 14]])
end
alias :fortnight :fortnights
-
+
# Reads best without arguments: 10.minutes.ago
- def ago(time = ::Time.now)
+ def ago(time = ::Time.current)
time - self
end
@@ -68,7 +68,7 @@ class Numeric
alias :until :ago
# Reads best with argument: 10.minutes.since(time)
- def since(time = ::Time.now)
+ def since(time = ::Time.current)
time + self
end
diff --git a/activesupport/lib/active_support/core_ext/object.rb b/activesupport/lib/active_support/core_ext/object.rb
index d671da6711..790a26f5c1 100644
--- a/activesupport/lib/active_support/core_ext/object.rb
+++ b/activesupport/lib/active_support/core_ext/object.rb
@@ -2,7 +2,6 @@ require 'active_support/core_ext/object/acts_like'
require 'active_support/core_ext/object/blank'
require 'active_support/core_ext/object/duplicable'
require 'active_support/core_ext/object/try'
-require 'active_support/core_ext/object/returning'
require 'active_support/core_ext/object/conversions'
require 'active_support/core_ext/object/instance_variables'
diff --git a/activesupport/lib/active_support/core_ext/object/blank.rb b/activesupport/lib/active_support/core_ext/object/blank.rb
index eb99bb1a36..d0c1ea8326 100644
--- a/activesupport/lib/active_support/core_ext/object/blank.rb
+++ b/activesupport/lib/active_support/core_ext/object/blank.rb
@@ -13,11 +13,11 @@ class Object
respond_to?(:empty?) ? empty? : !self
end
- # An object is present if it's not blank.
+ # An object is present if it's not <tt>blank?</tt>.
def present?
!blank?
end
-
+
# Returns object if it's #present? otherwise returns nil.
# object.presence is equivalent to object.present? ? object : nil.
#
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 866317b17a..eda9694614 100644
--- a/activesupport/lib/active_support/core_ext/object/instance_variables.rb
+++ b/activesupport/lib/active_support/core_ext/object/instance_variables.rb
@@ -7,13 +7,10 @@ class Object
# @x, @y = x, y
# end
# end
- #
+ #
# C.new(0, 1).instance_values # => {"x" => 0, "y" => 1}
def instance_values #:nodoc:
- instance_variables.inject({}) do |values, name|
- values[name.to_s[1..-1]] = instance_variable_get(name)
- values
- end
+ Hash[instance_variables.map { |name| [name.to_s[1..-1], instance_variable_get(name)] }]
end
# Returns an array of instance variable names including "@". They are strings
@@ -24,7 +21,7 @@ class Object
# @x, @y = x, y
# end
# end
- #
+ #
# C.new(0, 1).instance_variable_names # => ["@y", "@x"]
if RUBY_VERSION >= '1.9'
def instance_variable_names
@@ -33,35 +30,4 @@ class Object
else
alias_method :instance_variable_names, :instance_variables
end
-
- # Copies the instance variables of +object+ into +self+.
- #
- # Instance variable names in the +exclude+ array are ignored. If +object+
- # responds to <tt>protected_instance_variables</tt> the ones returned are
- # also ignored. For example, Rails controllers implement that method.
- #
- # In both cases strings and symbols are understood, and they have to include
- # the at sign.
- #
- # class C
- # def initialize(x, y, z)
- # @x, @y, @z = x, y, z
- # end
- #
- # def protected_instance_variables
- # %w(@z)
- # end
- # end
- #
- # a = C.new(0, 1, 2)
- # b = C.new(3, 4, 5)
- #
- # a.copy_instance_variables_from(b, [:@y])
- # # a is now: @x = 3, @y = 1, @z = 2
- def copy_instance_variables_from(object, exclude = []) #:nodoc:
- exclude += object.protected_instance_variables if object.respond_to? :protected_instance_variables
-
- vars = object.instance_variables.map(&:to_s) - exclude.map(&:to_s)
- vars.each { |name| instance_variable_set(name, object.instance_variable_get(name)) }
- end
end
diff --git a/activesupport/lib/active_support/core_ext/object/returning.rb b/activesupport/lib/active_support/core_ext/object/returning.rb
deleted file mode 100644
index 07250b2a27..0000000000
--- a/activesupport/lib/active_support/core_ext/object/returning.rb
+++ /dev/null
@@ -1,43 +0,0 @@
-class Object
- # Returns +value+ after yielding +value+ to the block. This simplifies the
- # process of constructing an object, performing work on the object, and then
- # returning the object from a method. It is a Ruby-ized realization of the K
- # combinator, courtesy of Mikael Brockman.
- #
- # ==== Examples
- #
- # # Without returning
- # def foo
- # values = []
- # values << "bar"
- # values << "baz"
- # return values
- # end
- #
- # foo # => ['bar', 'baz']
- #
- # # returning with a local variable
- # def foo
- # returning values = [] do
- # values << 'bar'
- # values << 'baz'
- # end
- # end
- #
- # foo # => ['bar', 'baz']
- #
- # # returning with a block argument
- # def foo
- # returning [] do |values|
- # values << 'bar'
- # values << 'baz'
- # end
- # end
- #
- # foo # => ['bar', 'baz']
- def returning(value)
- ActiveSupport::Deprecation.warn('Object#returning has been deprecated in favor of Object#tap.', caller)
- yield(value)
- value
- end
-end \ No newline at end of file
diff --git a/activesupport/lib/active_support/core_ext/object/to_param.rb b/activesupport/lib/active_support/core_ext/object/to_param.rb
index f2e7c2351e..e5f81078ee 100644
--- a/activesupport/lib/active_support/core_ext/object/to_param.rb
+++ b/activesupport/lib/active_support/core_ext/object/to_param.rb
@@ -1,5 +1,3 @@
-
-
class Object
# Alias of <tt>to_s</tt>.
def to_param
@@ -34,16 +32,24 @@ class Array
end
class Hash
- # Converts a hash into a string suitable for use as a URL query string. An optional <tt>namespace</tt> can be
- # passed to enclose the param names (see example below).
+ # Returns a string representation of the receiver suitable for use as a URL
+ # query string:
+ #
+ # {:name => 'David', :nationality => 'Danish'}.to_param
+ # # => "name=David&nationality=Danish"
+ #
+ # An optional namespace can be passed to enclose the param names:
+ #
+ # {:name => 'David', :nationality => 'Danish'}.to_param('user')
+ # # => "user[name]=David&user[nationality]=Danish"
#
- # ==== Examples
- # { :name => 'David', :nationality => 'Danish' }.to_param # => "name=David&nationality=Danish"
+ # The string pairs "key=value" that conform the query string
+ # are sorted lexicographically in ascending order.
#
- # { :name => 'David', :nationality => 'Danish' }.to_param('user') # => "user[name]=David&user[nationality]=Danish"
+ # This method is also aliased as +to_query+.
def to_param(namespace = nil)
collect do |key, value|
value.to_query(namespace ? "#{namespace}[#{key}]" : key)
- end * '&'
+ end.sort * '&'
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 c9981895b4..3f1540f685 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).gsub(/%(5B|5D)/n) { [$1].pack('H*') }}=#{CGI.escape(to_param.to_s)}"
+ "#{CGI.escape(key.to_s)}=#{CGI.escape(to_param.to_s)}"
end
end
@@ -15,7 +15,7 @@ class Array
# Converts an array into a string suitable for use as a URL query string,
# using the given +key+ as the param name.
#
- # ['Rails', 'coding'].to_query('hobbies') # => "hobbies[]=Rails&hobbies[]=coding"
+ # ['Rails', 'coding'].to_query('hobbies') # => "hobbies%5B%5D=Rails&hobbies%5B%5D=coding"
def to_query(key)
prefix = "#{key}[]"
collect { |value| value.to_query(prefix) }.join '&'
diff --git a/activesupport/lib/active_support/core_ext/object/try.rb b/activesupport/lib/active_support/core_ext/object/try.rb
index a1c63a0e54..04619124a1 100644
--- a/activesupport/lib/active_support/core_ext/object/try.rb
+++ b/activesupport/lib/active_support/core_ext/object/try.rb
@@ -5,6 +5,8 @@ class Object
# *Unlike* that method however, a +NoMethodError+ exception will *not* be raised
# and +nil+ will be returned instead, if the receiving object is a +nil+ object or NilClass.
#
+ # If try is called without a method to call, it will yield any given block with the object.
+ #
# ==== Examples
#
# Without try
@@ -18,15 +20,18 @@ class Object
# +try+ also accepts arguments and/or a block, for the method it is trying
# Person.try(:find, 1)
# @people.try(:collect) {|p| p.name}
+ #
+ # Without a method argument try will yield to the block unless the receiver is nil.
+ # @person.try { |p| "#{p.first_name} #{p.last_name}" }
#--
- # This method definition below is for rdoc purposes only. The alias_method call
- # below overrides it as an optimization since +try+ behaves like +Object#send+,
- # unless called on +NilClass+.
- def try(method, *args, &block)
- send(method, *args, &block)
+ # +try+ behaves like +Object#send+, unless called on +NilClass+.
+ def try(*a, &b)
+ if a.empty? && block_given?
+ yield self
+ else
+ __send__(*a, &b)
+ end
end
- remove_method :try
- alias_method :try, :__send__
end
class NilClass #:nodoc:
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 3209cf7f11..c23afabfdb 100644
--- a/activesupport/lib/active_support/core_ext/object/with_options.rb
+++ b/activesupport/lib/active_support/core_ext/object/with_options.rb
@@ -7,13 +7,27 @@ class Object
# provided. Each method called on the block variable must take an options
# hash as its final argument.
#
- # with_options :order => 'created_at', :class_name => 'Comment' do |post|
- # post.has_many :comments, :conditions => ['approved = ?', true], :dependent => :delete_all
- # post.has_many :unapproved_comments, :conditions => ['approved = ?', false]
- # post.has_many :all_comments
+ # Without with_options, this code contains duplication:
+ #
+ # class Account < ActiveRecord::Base
+ # has_many :customers, :dependent => :destroy
+ # has_many :products, :dependent => :destroy
+ # has_many :invoices, :dependent => :destroy
+ # has_many :expenses, :dependent => :destroy
+ # end
+ #
+ # Using with_options, we can remove the duplication:
+ #
+ # class Account < ActiveRecord::Base
+ # with_options :dependent => :destroy do |assoc|
+ # assoc.has_many :customers
+ # assoc.has_many :products
+ # assoc.has_many :invoices
+ # assoc.has_many :expenses
+ # end
# end
#
- # Can also be used with an explicit receiver:
+ # It can also be used with an explicit receiver:
#
# map.with_options :controller => "people" do |people|
# people.connect "/people", :action => "index"
diff --git a/activesupport/lib/active_support/core_ext/range.rb b/activesupport/lib/active_support/core_ext/range.rb
index c0736f3a44..2428a02242 100644
--- a/activesupport/lib/active_support/core_ext/range.rb
+++ b/activesupport/lib/active_support/core_ext/range.rb
@@ -2,3 +2,4 @@ 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/conversions.rb b/activesupport/lib/active_support/core_ext/range/conversions.rb
index 11a7ff66de..544e63132d 100644
--- a/activesupport/lib/active_support/core_ext/range/conversions.rb
+++ b/activesupport/lib/active_support/core_ext/range/conversions.rb
@@ -6,7 +6,7 @@ class Range
# Gives a human readable format of the range.
#
# ==== Example
- #
+ #
# [1..100].to_formatted_s # => "1..100"
def to_formatted_s(format = :default)
if formatter = RANGE_FORMATS[format]
diff --git a/activesupport/lib/active_support/core_ext/range/cover.rb b/activesupport/lib/active_support/core_ext/range/cover.rb
new file mode 100644
index 0000000000..3a182cddd2
--- /dev/null
+++ b/activesupport/lib/active_support/core_ext/range/cover.rb
@@ -0,0 +1,3 @@
+class Range
+ alias_method(:cover?, :include?) unless instance_methods.include?(:cover?)
+end
diff --git a/activesupport/lib/active_support/core_ext/string.rb b/activesupport/lib/active_support/core_ext/string.rb
index d8d1f9436e..8fb8c31ade 100644
--- a/activesupport/lib/active_support/core_ext/string.rb
+++ b/activesupport/lib/active_support/core_ext/string.rb
@@ -9,4 +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' \ No newline at end of file
+require 'active_support/core_ext/string/encoding'
+require 'active_support/core_ext/string/strip'
diff --git a/activesupport/lib/active_support/core_ext/string/access.rb b/activesupport/lib/active_support/core_ext/string/access.rb
index 64bc8f6cea..c0d5cdf2d5 100644
--- a/activesupport/lib/active_support/core_ext/string/access.rb
+++ b/activesupport/lib/active_support/core_ext/string/access.rb
@@ -4,27 +4,27 @@ 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:
+ # 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:
+ # 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:
+ # Examples:
# "hello".to(0) # => "h"
# "hello".to(2) # => "hel"
# "hello".to(10) # => "hello"
@@ -34,7 +34,7 @@ class String
# Returns the first character of the string or the first +limit+ characters.
#
- # Examples:
+ # Examples:
# "hello".first # => "h"
# "hello".first(2) # => "he"
# "hello".first(10) # => "hello"
@@ -50,7 +50,7 @@ class String
# Returns the last character of the string or the last +limit+ characters.
#
- # Examples:
+ # Examples:
# "hello".last # => "o"
# "hello".last(2) # => "lo"
# "hello".last(10) # => "hello"
diff --git a/activesupport/lib/active_support/core_ext/string/filters.rb b/activesupport/lib/active_support/core_ext/string/filters.rb
index e15a1df9c9..d478ee0ef6 100644
--- a/activesupport/lib/active_support/core_ext/string/filters.rb
+++ b/activesupport/lib/active_support/core_ext/string/filters.rb
@@ -25,13 +25,13 @@ class String
# "Once upon a time in a world far far away".truncate(27)
# # => "Once upon a time in a wo..."
#
- # The last characters will be replaced with the <tt>:omission</tt> string (defaults to "...")
- # for a total length not exceeding <tt>:length</tt>:
+ # Pass a <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..."
#
- # Pass a <tt>:separator</tt> to truncate +text+ at a natural break:
+ # 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 f... (continued)"
diff --git a/activesupport/lib/active_support/core_ext/string/inflections.rb b/activesupport/lib/active_support/core_ext/string/inflections.rb
index 32913a06ad..2f0676f567 100644
--- a/activesupport/lib/active_support/core_ext/string/inflections.rb
+++ b/activesupport/lib/active_support/core_ext/string/inflections.rb
@@ -1,7 +1,9 @@
require 'active_support/inflector/methods'
require 'active_support/inflector/inflections'
+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 database from the name of a class.
+# For instance, you can figure out the name of a table from the name of a class.
#
# "ScaleScore".tableize # => "scale_scores"
#
@@ -72,7 +74,7 @@ class String
alias_method :titlecase, :titleize
# The reverse of +camelize+. Makes an underscored, lowercase form from the expression in the string.
- #
+ #
# +underscore+ will also change '::' to '/' to convert namespaces to paths.
#
# "ActiveRecord".underscore # => "active_record"
@@ -97,7 +99,7 @@ class String
end
# Replaces special characters in a string so that it may be used as part of a 'pretty' URL.
- #
+ #
# ==== Examples
#
# class Person
@@ -105,10 +107,10 @@ class String
# "#{id}-#{name.parameterize}"
# end
# end
- #
+ #
# @person = Person.find(1)
# # => #<Person id: 1, name: "Donald E. Knuth">
- #
+ #
# <%= link_to(@person.name, person_path %>
# # => <a href="/person/1-donald-e-knuth">Donald E. Knuth</a>
def parameterize(sep = '-')
@@ -138,11 +140,11 @@ class String
def classify
ActiveSupport::Inflector.classify(self)
end
-
+
# Capitalizes the first word, turns underscores into spaces, and strips '_id'.
# Like +titleize+, this is meant for creating pretty output.
#
- # "employee_salary" # => "Employee salary"
+ # "employee_salary" # => "Employee salary"
# "author_id" # => "Author"
def humanize
ActiveSupport::Inflector.humanize(self)
diff --git a/activesupport/lib/active_support/core_ext/string/multibyte.rb b/activesupport/lib/active_support/core_ext/string/multibyte.rb
index 0b974f5e0a..41de4d6435 100644
--- a/activesupport/lib/active_support/core_ext/string/multibyte.rb
+++ b/activesupport/lib/active_support/core_ext/string/multibyte.rb
@@ -9,7 +9,7 @@ class String
#
# 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 encapsuled string.
+ # 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"
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 b53929c2a3..addd4dab95 100644
--- a/activesupport/lib/active_support/core_ext/string/output_safety.rb
+++ b/activesupport/lib/active_support/core_ext/string/output_safety.rb
@@ -24,6 +24,7 @@ class ERB
end
end
+ # Aliasing twice issues a warning "discarding old...". Remove first to avoid it.
remove_method(:h)
alias h html_escape
@@ -32,15 +33,23 @@ class ERB
singleton_class.send(:remove_method, :html_escape)
module_function :html_escape
- # A utility method for escaping HTML entities in JSON strings.
- # This method is also aliased as <tt>j</tt>.
+ # 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?")
+ # # => is a \u003E 0 \u0026 a \u003C 10?
+ #
+ # Note that after this operation is performed the output is not
+ # valid JSON. In particular double quotes are removed:
+ #
+ # json_escape('{"name":"john","created_at":"2010-04-28T01:39:31Z","id":1}')
+ # # => {name:john,created_at:2010-04-28T01:39:31Z,id:1}
+ #
+ # This method is also aliased as +j+, and available as a helper
+ # in Rails templates:
#
- # In your ERb templates, use this method to escape any HTML entities:
# <%=j @person.to_json %>
#
- # ==== Example:
- # puts json_escape("is a > 0 & a < 10?")
- # # => is a \u003E 0 \u0026 a \u003C 10?
def json_escape(s)
s.to_s.gsub(/[&"><]/) { |special| JSON_ESCAPE[special] }
end
@@ -92,7 +101,13 @@ module ActiveSupport #:nodoc:
self
end
+ def encode_with(coder)
+ 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
end
diff --git a/activesupport/lib/active_support/core_ext/string/strip.rb b/activesupport/lib/active_support/core_ext/string/strip.rb
new file mode 100644
index 0000000000..086c610976
--- /dev/null
+++ b/activesupport/lib/active_support/core_ext/string/strip.rb
@@ -0,0 +1,26 @@
+require 'active_support/core_ext/object/try'
+
+class String
+ # Strips indentation in heredocs.
+ #
+ # For example in
+ #
+ # if options[:usage]
+ # puts <<-USAGE.strip_heredoc
+ # This command does such and such.
+ #
+ # Supported options are:
+ # -h This message
+ # ...
+ # USAGE
+ # end
+ #
+ # the user would see the usage message aligned against the left margin.
+ #
+ # Technically, it looks for the least indented line in the whole string, and removes
+ # that amount of leading whitespace.
+ def strip_heredoc
+ indent = scan(/^[ \t]*(?=\S)/).min.try(:size) || 0
+ gsub(/^[ \t]{#{indent}}/, '')
+ 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 90b6cd3685..7e134db118 100644
--- a/activesupport/lib/active_support/core_ext/time/calculations.rb
+++ b/activesupport/lib/active_support/core_ext/time/calculations.rb
@@ -1,7 +1,4 @@
require 'active_support/duration'
-require 'active_support/core_ext/date/acts_like'
-require 'active_support/core_ext/date/calculations'
-require 'active_support/core_ext/date_time/conversions'
class Time
COMMON_YEAR_DAYS_IN_MONTH = [nil, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31]
@@ -22,7 +19,7 @@ class Time
# Returns a new Time if requested year can be accommodated by Ruby's Time class
# (i.e., if year is within either 1970..2038 or 1902..2038, depending on system architecture);
- # otherwise returns a DateTime
+ # 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.
@@ -41,9 +38,9 @@ class Time
time_with_datetime_fallback(:local, *args)
end
- # Returns <tt>Time.zone.now</tt> when <tt>config.time_zone</tt> is set, otherwise just returns <tt>Time.now</tt>.
+ # Returns <tt>Time.zone.now</tt> when <tt>Time.zone</tt> or <tt>config.time_zone</tt> are set, otherwise just returns <tt>Time.now</tt>.
def current
- ::Time.zone_default ? ::Time.zone.now : ::Time.now
+ ::Time.zone ? ::Time.zone.now : ::Time.now
end
end
@@ -92,12 +89,12 @@ class Time
options[:weeks], partial_weeks = options[:weeks].divmod(1)
options[:days] = (options[: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
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
@@ -117,6 +114,11 @@ class Time
end
alias :in :since
+ # Returns a new Time representing the time a number of specified weeks ago.
+ def weeks_ago(weeks)
+ advance(:weeks => -weeks)
+ end
+
# Returns a new Time representing the time a number of specified months ago
def months_ago(months)
advance(:months => -months)
@@ -172,6 +174,11 @@ class Time
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).
+ def prev_week(day = :monday)
+ ago(1.week).beginning_of_week.since(DAYS_INTO_WEEK[day].day).change(:hour => 0)
+ end
+
# 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)
@@ -273,14 +280,8 @@ class Time
# Layers additional behavior on Time#<=> so that DateTime and ActiveSupport::TimeWithZone instances
# can be chronologically compared with a Time
def compare_with_coercion(other)
- # if other is an ActiveSupport::TimeWithZone, coerce a Time instance from it so we can do <=> comparison
- other = other.comparable_time if other.respond_to?(:comparable_time)
- if other.acts_like?(:date)
- # other is a Date/DateTime, so coerce self #to_datetime and hand off to DateTime#<=>
- to_datetime.compare_without_coercion(other)
- else
- compare_without_coercion(other)
- end
+ # we're avoiding Time#to_datetime cause it's expensive
+ other.is_a?(Time) ? compare_without_coercion(other.to_time) : to_datetime <=> other
end
alias_method :compare_without_coercion, :<=>
alias_method :<=>, :compare_with_coercion
diff --git a/activesupport/lib/active_support/core_ext/time/conversions.rb b/activesupport/lib/active_support/core_ext/time/conversions.rb
index 86103ebce2..d9d5e02778 100644
--- a/activesupport/lib/active_support/core_ext/time/conversions.rb
+++ b/activesupport/lib/active_support/core_ext/time/conversions.rb
@@ -1,4 +1,4 @@
-require 'active_support/inflector'
+require 'active_support/inflector/methods'
require 'active_support/core_ext/time/publicize_conversion_methods'
require 'active_support/values/time_zone'
@@ -19,8 +19,8 @@ class Time
#
# time = Time.now # => Thu Jan 18 06:10:17 CST 2007
#
- # time.to_formatted_s(:time) # => "06:10:17"
- # time.to_s(:time) # => "06:10:17"
+ # time.to_formatted_s(:time) # => "06:10"
+ # time.to_s(:time) # => "06:10"
#
# time.to_formatted_s(:db) # => "2007-01-18 06:10:17"
# time.to_formatted_s(:number) # => "20070118061017"
@@ -46,7 +46,7 @@ class Time
end
alias_method :to_default_s, :to_s
alias_method :to_s, :to_formatted_s
-
+
# Returns the UTC offset as an +HH:MM formatted string.
#
# Time.local(2000).formatted_offset # => "-06:00"
@@ -55,31 +55,9 @@ class Time
utc? && alternate_utc_string || ActiveSupport::TimeZone.seconds_to_utc_offset(utc_offset, colon)
end
- # Converts a Time object to a Date, dropping hour, minute, and second precision.
- #
- # my_time = Time.now # => Mon Nov 12 22:59:51 -0500 2007
- # my_time.to_date # => Mon, 12 Nov 2007
- #
- # your_time = Time.parse("1/13/2009 1:13:03 P.M.") # => Tue Jan 13 13:13:03 -0500 2009
- # your_time.to_date # => Tue, 13 Jan 2009
- def to_date
- ::Date.new(year, month, day)
- end unless method_defined?(:to_date)
-
# A method to keep Time, Date and DateTime instances interchangeable on conversions.
# In this case, it simply returns +self+.
def to_time
self
end unless method_defined?(:to_time)
-
- # Converts a Time instance to a Ruby DateTime instance, preserving UTC offset.
- #
- # my_time = Time.now # => Mon Nov 12 23:04:21 -0500 2007
- # my_time.to_datetime # => Mon, 12 Nov 2007 23:04:21 -0500
- #
- # your_time = Time.parse("1/13/2009 1:13:03 P.M.") # => Tue Jan 13 13:13:03 -0500 2009
- # your_time.to_datetime # => Tue, 13 Jan 2009 13:13:03 -0500
- def to_datetime
- ::DateTime.civil(year, month, day, hour, min, sec, Rational(utc_offset, 86400))
- end unless method_defined?(:to_datetime)
end
diff --git a/activesupport/lib/active_support/core_ext/time/zones.rb b/activesupport/lib/active_support/core_ext/time/zones.rb
index 6b9ee84d5c..ff90d7ca58 100644
--- a/activesupport/lib/active_support/core_ext/time/zones.rb
+++ b/activesupport/lib/active_support/core_ext/time/zones.rb
@@ -4,13 +4,13 @@ class Time
class << self
attr_accessor :zone_default
- # Returns the TimeZone for the current request, if this has been set (via Time.zone=).
+ # Returns the TimeZone for the current request, if this has been set (via Time.zone=).
# If <tt>Time.zone</tt> has not been set for the current request, returns the TimeZone specified in <tt>config.time_zone</tt>.
def zone
Thread.current[:time_zone] || zone_default
end
- # Sets <tt>Time.zone</tt> to a TimeZone object for the current request/thread.
+ # Sets <tt>Time.zone</tt> to a TimeZone object for the current request/thread.
#
# This method accepts any of the following:
#
@@ -19,14 +19,18 @@ class Time
# * A TZInfo::Timezone object.
# * An identifier for a TZInfo::Timezone object (e.g., "America/New_York").
#
- # Here's an example of how you might set <tt>Time.zone</tt> on a per request basis -- <tt>current_user.time_zone</tt>
- # just needs to return a string identifying the user's preferred TimeZone:
+ # Here's an example of how you might set <tt>Time.zone</tt> on a per request basis and reset it when the request is done.
+ # <tt>current_user.time_zone</tt> just needs to return a string identifying the user's preferred time zone:
#
# class ApplicationController < ActionController::Base
- # before_filter :set_time_zone
+ # around_filter :set_time_zone
#
# def set_time_zone
- # Time.zone = current_user.time_zone
+ # old_time_zone = Time.zone
+ # Time.zone = current_user.time_zone if logged_in?
+ # yield
+ # ensure
+ # Time.zone = old_time_zone
# end
# end
def zone=(time_zone)
@@ -63,7 +67,7 @@ class Time
# This method is similar to Time#localtime, except that it uses <tt>Time.zone</tt> as the local zone
# instead of the operating system's time zone.
#
- # You can also pass in a TimeZone instance or string that identifies a TimeZone as an argument,
+ # You can also pass in a TimeZone instance or string that identifies a TimeZone as an argument,
# and the conversion will be based on that zone instead of <tt>Time.zone</tt>.
#
# Time.utc(2000).in_time_zone('Alaska') # => Fri, 31 Dec 1999 15:00:00 AKST -09:00
diff --git a/activesupport/lib/active_support/core_ext/uri.rb b/activesupport/lib/active_support/core_ext/uri.rb
index b7fe0a6209..ee991e3439 100644
--- a/activesupport/lib/active_support/core_ext/uri.rb
+++ b/activesupport/lib/active_support/core_ext/uri.rb
@@ -20,3 +20,11 @@ if RUBY_VERSION >= '1.9'
end
end
end
+
+module URI
+ class << self
+ def parser
+ @parser ||= URI.const_defined?(:Parser) ? URI::Parser.new : URI
+ end
+ end
+end
diff --git a/activesupport/lib/active_support/dependencies.rb b/activesupport/lib/active_support/dependencies.rb
index 2b80bd214f..dc10f78104 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/deprecation'
require 'active_support/core_ext/object/blank'
require 'active_support/core_ext/load_error'
require 'active_support/core_ext/name_error'
@@ -47,9 +48,6 @@ module ActiveSupport #:nodoc:
mattr_accessor :autoloaded_constants
self.autoloaded_constants = []
- mattr_accessor :references
- self.references = {}
-
# An array of constant names that need to be unloaded on every request. Used
# to allow arbitrary constants to be marked for unloading.
mattr_accessor :explicitly_unloadable_constants
@@ -63,58 +61,78 @@ module ActiveSupport #:nodoc:
mattr_accessor :log_activity
self.log_activity = false
- class WatchStack < Array
+ # The WatchStack keeps a stack of the modules being watched as files are loaded.
+ # If a file in the process of being loaded (parent.rb) triggers the load of
+ # another file (child.rb) the stack will ensure that child.rb handles the new
+ # constants.
+ #
+ # If child.rb is being autoloaded, its constants will be added to
+ # autoloaded_constants. If it was being `require`d, they will be discarded.
+ #
+ # 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
+ # @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
- @mutex = Mutex.new
+ @watching = []
+ super { |h,k| h[k] = [] }
end
- def self.locked(*methods)
- methods.each { |m| class_eval "def #{m}(*) lock { super } end", __FILE__, __LINE__ }
- end
+ # return a list of new constants found since the last call to watch_namespaces
+ def new_constants
+ constants = []
- locked :concat, :each, :delete_if, :<<
+ # Grab the list of namespaces that we're looking for new constants under
+ @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
- def new_constants_for(frames)
- constants = []
- frames.each do |mod_name, prior_constants|
- mod = Inflector.constantize(mod_name) if Dependencies.qualified_const_defined?(mod_name)
+ mod = Inflector.constantize(namespace) if Dependencies.qualified_const_defined?(namespace)
next unless mod.is_a?(Module)
- new_constants = mod.local_constant_names - prior_constants
-
- # If we are checking for constants under, say, :Object, nested under something
- # else that is checking for constants also under :Object, make sure the
- # parent knows that we have found, and taken care of, the constant.
- #
- # In particular, this means that since Kernel.require discards the constants
- # it finds, parents will be notified that about those constants, and not
- # consider them "new". As a result, they will not be added to the
- # autoloaded_constants list.
- each do |key, value|
- value.concat(new_constants) if key == mod_name
+ # Get a list of the constants that were added
+ new_constants = mod.local_constant_names - 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|
+ namespace_constants.concat(new_constants)
end
+ # Normalize the list of new constants, and add them to the list we will return
new_constants.each do |suffix|
- constants << ([mod_name, suffix] - ["Object"]).join("::")
+ constants << ([namespace, suffix] - ["Object"]).join("::")
end
end
constants
+ ensure
+ # A call to new_constants is always called after a call to watch_namespaces
+ pop_modules(@watching.pop)
end
# Add a set of modules to the watch stack, remembering the initial constants
- def add_modules(modules)
- list = modules.map do |desc|
- name = Dependencies.to_constant_name(desc)
- consts = Dependencies.qualified_const_defined?(name) ?
- Inflector.constantize(name).local_constant_names : []
- [name, consts]
+ def watch_namespaces(namespaces)
+ 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 : []
+
+ watching << module_name
+ self[module_name] << original_constants
end
- concat(list)
- list
+ @watching << watching
end
- def lock
- @mutex.synchronize { yield self }
+ def pop_modules(modules)
+ modules.each { |mod| self[mod].pop }
end
end
@@ -146,7 +164,7 @@ module ActiveSupport #:nodoc:
def const_missing(const_name, nesting = nil)
klass_name = name.presence || "Object"
- if !nesting
+ unless nesting
# We'll assume that the nesting of Foo::Bar is ["Foo::Bar", "Foo"]
# even though it might not be, such as in the case of
# class Foo::Bar; Baz; end
@@ -311,7 +329,7 @@ module ActiveSupport #:nodoc:
if load?
log "loading #{file_name}"
- # Enable warnings iff this file has not been loaded before and
+ # Enable warnings if this file has not been loaded before and
# warnings_on_first_load is set.
load_args = ["#{file_name}.rb"]
load_args << const_path unless const_path.nil?
@@ -491,7 +509,12 @@ module ActiveSupport #:nodoc:
end
# Remove the constants that have been autoloaded, and those that have been
- # marked for unloading.
+ # marked for unloading. Before each constant is removed a callback is sent
+ # to its class/module if it implements +before_remove_const+.
+ #
+ # The callback implementation should be restricted to cleaning up caches, etc.
+ # as the environment will be in an inconsistent state, e.g. other constants
+ # may have already been unloaded and not accessible.
def remove_unloadable_constants!
autoloaded_constants.each { |const| remove_constant const }
autoloaded_constants.clear
@@ -499,31 +522,76 @@ module ActiveSupport #:nodoc:
explicitly_unloadable_constants.each { |const| remove_constant const }
end
- class Reference
- @@constants = Hash.new { |h, k| h[k] = Inflector.constantize(k) }
+ class ClassCache
+ def initialize
+ @store = Hash.new { |h, k| h[k] = Inflector.constantize(k) }
+ end
- attr_reader :name
+ def empty?
+ @store.empty?
+ end
- def initialize(name)
- @name = name.to_s
- @@constants[@name] = name if name.respond_to?(:name)
+ def key?(key)
+ @store.key?(key)
end
- def get
- @@constants[@name]
+ def []=(key, value)
+ return unless key.respond_to?(:name)
+
+ raise(ArgumentError, 'anonymous classes cannot be cached') if key.name.blank?
+
+ @store[key.name] = value
end
- def self.clear!
- @@constants.clear
+ def [](key)
+ key = key.name if key.respond_to?(:name)
+
+ @store[key]
+ end
+ alias :get :[]
+
+ class Getter # :nodoc:
+ def initialize(name)
+ @name = name
+ end
+
+ def get
+ Reference.get @name
+ end
+ deprecate :get
+ end
+
+ def new(name)
+ self[name] = name
+ Getter.new(name)
+ end
+ deprecate :new
+
+ def store(name)
+ self[name] = name
+ self
+ end
+
+ def clear!
+ @store.clear
end
end
+ Reference = ClassCache.new
+
def ref(name)
- references[name] ||= Reference.new(name)
+ Reference.new(name)
+ end
+ deprecate :ref
+
+ # Store a reference to a class +klass+.
+ def reference(klass)
+ Reference.store klass
end
+ # Get the reference for class named +name+.
def constantize(name)
- ref(name).get
+ Reference.get(name)
end
# Determine if the given constant has been automatically loaded.
@@ -563,14 +631,15 @@ module ActiveSupport #:nodoc:
# and will be removed immediately.
def new_constants_in(*descs)
log_call(*descs)
- watch_frames = constant_watch_stack.add_modules(descs)
+ constant_watch_stack.watch_namespaces(descs)
aborting = true
+
begin
yield # Now yield to the code that is to define new constants.
aborting = false
ensure
- new_constants = constant_watch_stack.new_constants_for(watch_frames)
+ new_constants = constant_watch_stack.new_constants
log "New constants: #{new_constants * ', '}"
return new_constants unless aborting
@@ -580,9 +649,6 @@ module ActiveSupport #:nodoc:
end
return []
- ensure
- # Remove the stack frames that we added.
- watch_frames.each {|f| constant_watch_stack.delete(f) } if watch_frames.present?
end
class LoadingModule #:nodoc:
@@ -618,6 +684,8 @@ module ActiveSupport #:nodoc:
parent = Inflector.constantize(names * '::')
log "removing constant #{const}"
+ constantized = constantize(const)
+ constantized.before_remove_const if constantized.respond_to?(:before_remove_const)
parent.instance_eval { remove_const to_remove }
return true
@@ -625,7 +693,7 @@ module ActiveSupport #:nodoc:
protected
def log_call(*args)
- if logger && log_activity
+ if log_activity?
arg_str = args.collect { |arg| arg.inspect } * ', '
/in `([a-z_\?\!]+)'/ =~ caller(1).first
selector = $1 || '<unknown>'
@@ -634,9 +702,11 @@ module ActiveSupport #:nodoc:
end
def log(msg)
- if logger && log_activity
- logger.debug "Dependencies: #{msg}"
- end
+ logger.debug "Dependencies: #{msg}" if log_activity?
+ end
+
+ def log_activity?
+ logger && log_activity
end
end
end
diff --git a/activesupport/lib/active_support/deprecation.rb b/activesupport/lib/active_support/deprecation.rb
index e1b8211d68..ce0775a690 100644
--- a/activesupport/lib/active_support/deprecation.rb
+++ b/activesupport/lib/active_support/deprecation.rb
@@ -4,12 +4,12 @@ require 'active_support/deprecation/method_wrappers'
require 'active_support/deprecation/proxy_wrappers'
module ActiveSupport
- module Deprecation #:nodoc:
+ module Deprecation
class << self
# The version the deprecated behavior will be removed, by default.
attr_accessor :deprecation_horizon
end
- self.deprecation_horizon = '3.0'
+ self.deprecation_horizon = '3.1'
# By default, warnings are not silenced and debugging is off.
self.silenced = false
diff --git a/activesupport/lib/active_support/deprecation/behaviors.rb b/activesupport/lib/active_support/deprecation/behaviors.rb
index f54f65dcf0..da4af339fc 100644
--- a/activesupport/lib/active_support/deprecation/behaviors.rb
+++ b/activesupport/lib/active_support/deprecation/behaviors.rb
@@ -7,10 +7,17 @@ module ActiveSupport
# Whether to print a backtrace along with the warning.
attr_accessor :debug
+ # Returns the set behaviour or if one isn't set, defaults to +:stderr+
def behavior
@behavior ||= [DEFAULT_BEHAVIORS[:stderr]]
end
+ # Sets the behaviour to the specified value. Can be a single value or an array.
+ #
+ # Examples
+ #
+ # ActiveSupport::Deprecation.behavior = :stderr
+ # ActiveSupport::Deprecation.behavior = [:stderr, :log]
def behavior=(behavior)
@behavior = Array.wrap(behavior).map { |b| DEFAULT_BEHAVIORS[b] || b }
end
diff --git a/activesupport/lib/active_support/deprecation/proxy_wrappers.rb b/activesupport/lib/active_support/deprecation/proxy_wrappers.rb
index deb33ab702..a65fcafb44 100644
--- a/activesupport/lib/active_support/deprecation/proxy_wrappers.rb
+++ b/activesupport/lib/active_support/deprecation/proxy_wrappers.rb
@@ -1,4 +1,4 @@
-require 'active_support/inflector'
+require 'active_support/inflector/methods'
module ActiveSupport
module Deprecation
@@ -58,7 +58,7 @@ module ActiveSupport
end
end
- class DeprecatedConstantProxy < DeprecationProxy #:nodoc:
+ class DeprecatedConstantProxy < DeprecationProxy #:nodoc:all
def initialize(old_const, new_const)
@old_const = old_const
@new_const = new_const
diff --git a/activesupport/lib/active_support/deprecation/reporting.rb b/activesupport/lib/active_support/deprecation/reporting.rb
index 49d58cd3a1..ced08b8783 100644
--- a/activesupport/lib/active_support/deprecation/reporting.rb
+++ b/activesupport/lib/active_support/deprecation/reporting.rb
@@ -3,6 +3,12 @@ module ActiveSupport
class << self
attr_accessor :silenced
+ # Outputs a deprecation warning to the output configured by <tt>ActiveSupport::Deprecation.behavior</tt>
+ #
+ # Example:
+ #
+ # ActiveSupport::Deprecation.warn("something broke!")
+ # #=> "DEPRECATION WARNING: something broke! (called from your_code.rb:1)"
def warn(message = nil, callstack = caller)
return if silenced
deprecation_message(callstack, message).tap do |m|
@@ -46,10 +52,14 @@ module ActiveSupport
end
def extract_callstack(callstack)
- if md = callstack.first.match(/^(.+?):(\d+)(?::in `(.*?)')?/)
- md.captures
- else
- callstack.first
+ rails_gem_root = File.expand_path("../../../../..", __FILE__) + "/"
+ offending_line = callstack.find { |line| !line.start_with?(rails_gem_root) } || callstack.first
+ if offending_line
+ if md = offending_line.match(/^(.+?):(\d+)(?::in `(.*?)')?/)
+ md.captures
+ else
+ offending_line
+ end
end
end
end
diff --git a/activesupport/lib/active_support/duration.rb b/activesupport/lib/active_support/duration.rb
index 7da357730b..00c67a470d 100644
--- a/activesupport/lib/active_support/duration.rb
+++ b/activesupport/lib/active_support/duration.rb
@@ -3,7 +3,7 @@ require 'active_support/core_ext/array/conversions'
require 'active_support/core_ext/object/acts_like'
module ActiveSupport
- # Provides accurate date and time measurements using Date#advance and
+ # Provides accurate date and time measurements using Date#advance and
# Time#advance, respectively. It mainly supports the methods on Numeric.
# Example:
#
@@ -80,6 +80,10 @@ module ActiveSupport
parts.to_sentence(:locale => :en)
end
+ def as_json(options = nil) #:nodoc:
+ to_i
+ end
+
protected
def sum(sign, time = ::Time.current) #:nodoc:
@@ -99,7 +103,7 @@ module ActiveSupport
private
def method_missing(method, *args, &block) #:nodoc:
- value.send(method, *args)
+ value.send(method, *args, &block)
end
end
end
diff --git a/activesupport/lib/active_support/file_update_checker.rb b/activesupport/lib/active_support/file_update_checker.rb
index cd658fe173..a97e9d7daf 100644
--- a/activesupport/lib/active_support/file_update_checker.rb
+++ b/activesupport/lib/active_support/file_update_checker.rb
@@ -8,7 +8,7 @@ module ActiveSupport
# I18n.reload!
# end
#
- # ActionDispatch::Callbacks.to_prepare do
+ # ActionDispatch::Reloader.to_prepare do
# i18n_reloader.execute_if_updated
# end
#
diff --git a/activesupport/lib/active_support/file_watcher.rb b/activesupport/lib/active_support/file_watcher.rb
new file mode 100644
index 0000000000..81e63e76a7
--- /dev/null
+++ b/activesupport/lib/active_support/file_watcher.rb
@@ -0,0 +1,36 @@
+module ActiveSupport
+ class FileWatcher
+ class Backend
+ def initialize(path, watcher)
+ @watcher = watcher
+ @path = path
+ end
+
+ def trigger(files)
+ @watcher.trigger(files)
+ end
+ end
+
+ def initialize
+ @regex_matchers = {}
+ end
+
+ def watch(pattern, &block)
+ @regex_matchers[pattern] = block
+ end
+
+ def trigger(files)
+ trigger_files = Hash.new { |h,k| h[k] = Hash.new { |h2,k2| h2[k2] = [] } }
+
+ files.each do |file, state|
+ @regex_matchers.each do |pattern, block|
+ trigger_files[block][state] << file if pattern === file
+ end
+ end
+
+ trigger_files.each do |block, payload|
+ block.call payload
+ end
+ end
+ end
+end
diff --git a/activesupport/lib/active_support/gzip.rb b/activesupport/lib/active_support/gzip.rb
index 35a50e9a77..62f9c9aa2e 100644
--- a/activesupport/lib/active_support/gzip.rb
+++ b/activesupport/lib/active_support/gzip.rb
@@ -5,6 +5,10 @@ module ActiveSupport
# A convenient wrapper for the zlib standard library that allows compression/decompression of strings with gzip.
module Gzip
class Stream < StringIO
+ def initialize(*)
+ super
+ set_encoding "BINARY" if "".encoding_aware?
+ end
def close; rewind; end
end
@@ -22,4 +26,4 @@ module ActiveSupport
output.string
end
end
-end \ No newline at end of file
+end
diff --git a/activesupport/lib/active_support/hash_with_indifferent_access.rb b/activesupport/lib/active_support/hash_with_indifferent_access.rb
index eec5d4cf47..79a0de7940 100644
--- a/activesupport/lib/active_support/hash_with_indifferent_access.rb
+++ b/activesupport/lib/active_support/hash_with_indifferent_access.rb
@@ -1,8 +1,7 @@
-require 'active_support/core_ext/hash/indifferent_access'
require 'active_support/core_ext/hash/keys'
# This class has dubious semantics and we only have it so that
-# people can write params[:key] instead of params['key']
+# people can write <tt>params[:key]</tt> instead of <tt>params['key']</tt>
# and they get the same value for both keys.
module ActiveSupport
@@ -28,6 +27,12 @@ module ActiveSupport
end
end
+ def self.new_from_hash_copying_default(hash)
+ new(hash).tap do |new_hash|
+ new_hash.default = hash.default
+ end
+ end
+
alias_method :regular_writer, :[]= unless method_defined?(:regular_writer)
alias_method :regular_update, :update unless method_defined?(:regular_update)
@@ -40,6 +45,8 @@ module ActiveSupport
regular_writer(convert_key(key), convert_value(value))
end
+ alias_method :store, :[]=
+
# Updates the instantized hash with values from the second:
#
# hash_1 = HashWithIndifferentAccess.new
@@ -90,7 +97,9 @@ module ActiveSupport
# Returns an exact copy of the hash.
def dup
- HashWithIndifferentAccess.new(self)
+ self.class.new(self).tap do |new_hash|
+ new_hash.default = default
+ end
end
# Merges the instantized and the specified hashes together, giving precedence to the values from the second hash
@@ -100,9 +109,9 @@ module ActiveSupport
end
# Performs the opposite of merge, with the keys and values from the first hash taking precedence over the second.
- # This overloaded definition prevents returning a regular hash, if reverse_merge is called on a HashWithDifferentAccess.
+ # This overloaded definition prevents returning a regular hash, if reverse_merge is called on a <tt>HashWithDifferentAccess</tt>.
def reverse_merge(other_hash)
- super other_hash.with_indifferent_access
+ super self.class.new_from_hash_copying_default(other_hash)
end
def reverse_merge!(other_hash)
@@ -131,11 +140,10 @@ module ActiveSupport
end
def convert_value(value)
- case value
- when Hash
- value.with_indifferent_access
- when Array
- value.collect { |e| e.is_a?(Hash) ? e.with_indifferent_access : e }
+ if value.class == Hash
+ self.class.new_from_hash_copying_default(value)
+ elsif value.is_a?(Array)
+ value.dup.replace(value.map { |e| convert_value(e) })
else
value
end
diff --git a/activesupport/lib/active_support/i18n.rb b/activesupport/lib/active_support/i18n.rb
index 45b9d20c01..00ea8813dd 100644
--- a/activesupport/lib/active_support/i18n.rb
+++ b/activesupport/lib/active_support/i18n.rb
@@ -1,9 +1,9 @@
begin
require 'i18n'
+ require 'active_support/lazy_load_hooks'
rescue LoadError => e
$stderr.puts "You don't have i18n installed in your application. Please add it to your Gemfile and run bundle install"
raise e
end
I18n.load_path << "#{File.dirname(__FILE__)}/locale/en.yml"
-ActiveSupport.run_load_hooks(:i18n)
diff --git a/activesupport/lib/active_support/i18n_railtie.rb b/activesupport/lib/active_support/i18n_railtie.rb
index db09919fd3..a25e951080 100644
--- a/activesupport/lib/active_support/i18n_railtie.rb
+++ b/activesupport/lib/active_support/i18n_railtie.rb
@@ -14,19 +14,34 @@ module I18n
@reloader ||= ActiveSupport::FileUpdateChecker.new([]){ I18n.reload! }
end
- # Add I18n::Railtie.reloader to ActionDispatch callbacks. Since, at this
+ # 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
- ActionDispatch::Callbacks.to_prepare do
+ ActionDispatch::Reloader.to_prepare do
I18n::Railtie.reloader.execute_if_updated
end
end
- # Set the i18n configuration only after initialization since a lot of
+ # Set the i18n configuration after initialization since a lot of
# configuration is still usually done in application initializers.
config.after_initialize do |app|
+ I18n::Railtie.initialize_i18n(app)
+ end
+
+ # Trigger i18n config before any eager loading has happened
+ # so it's ready if any classes require it when eager loaded
+ config.before_eager_load do |app|
+ I18n::Railtie.initialize_i18n(app)
+ end
+
+ protected
+
+ # Setup i18n configuration
+ def self.initialize_i18n(app)
+ return if @i18n_inited
+
fallbacks = app.config.i18n.delete(:fallbacks)
app.config.i18n.each do |setting, value|
@@ -44,9 +59,9 @@ module I18n
reloader.paths.concat I18n.load_path
reloader.execute_if_updated
- end
- protected
+ @i18n_inited = true
+ end
def self.include_fallbacks_module
I18n.backend.class.send(:include, I18n::Backend::Fallbacks)
@@ -66,7 +81,7 @@ module I18n
I18n.fallbacks = I18n::Locale::Fallbacks.new(*args)
end
-
+
def self.validate_fallbacks(fallbacks)
case fallbacks
when ActiveSupport::OrderedOptions
@@ -78,4 +93,4 @@ module I18n
end
end
end
-end \ No newline at end of file
+end
diff --git a/activesupport/lib/active_support/inflections.rb b/activesupport/lib/active_support/inflections.rb
index e7b5387ed7..06ceccdb22 100644
--- a/activesupport/lib/active_support/inflections.rb
+++ b/activesupport/lib/active_support/inflections.rb
@@ -4,10 +4,12 @@ module ActiveSupport
inflect.plural(/s$/i, 's')
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')
inflect.plural(/(bu)s$/i, '\1ses')
inflect.plural(/(buffal|tomat)o$/i, '\1oes')
inflect.plural(/([ti])um$/i, '\1a')
+ inflect.plural(/([ti])a$/i, '\1a')
inflect.plural(/sis$/i, 'ses')
inflect.plural(/(?:([^f])fe|([lr])f)$/i, '\1\2ves')
inflect.plural(/(hive)$/i, '\1s')
@@ -15,7 +17,9 @@ module ActiveSupport
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(/^(ox)$/i, '\1en')
+ inflect.plural(/^(oxen)$/i, '\1')
inflect.plural(/(quiz)$/i, '\1zes')
inflect.singular(/s$/i, '')
diff --git a/activesupport/lib/active_support/inflector/inflections.rb b/activesupport/lib/active_support/inflector/inflections.rb
index 3caf78bc7d..e136e4c5b3 100644
--- a/activesupport/lib/active_support/inflector/inflections.rb
+++ b/activesupport/lib/active_support/inflector/inflections.rb
@@ -148,7 +148,7 @@ module ActiveSupport
def singularize(word)
result = word.to_s.dup
- if inflections.uncountables.any? { |inflection| result =~ /#{inflection}\Z/i }
+ if inflections.uncountables.any? { |inflection| result =~ /\b(#{inflection})\Z/i }
result
else
inflections.singulars.each { |(rule, replacement)| break if result.gsub!(rule, replacement) }
diff --git a/activesupport/lib/active_support/inflector/transliterate.rb b/activesupport/lib/active_support/inflector/transliterate.rb
index bccc5425a6..40e7a0e389 100644
--- a/activesupport/lib/active_support/inflector/transliterate.rb
+++ b/activesupport/lib/active_support/inflector/transliterate.rb
@@ -1,5 +1,6 @@
# encoding: utf-8
require 'active_support/core_ext/string/multibyte'
+require 'active_support/i18n'
module ActiveSupport
module Inflector
diff --git a/activesupport/lib/active_support/json/backends/jsongem.rb b/activesupport/lib/active_support/json/backends/jsongem.rb
index cfe28d7bb9..533ba25da3 100644
--- a/activesupport/lib/active_support/json/backends/jsongem.rb
+++ b/activesupport/lib/active_support/json/backends/jsongem.rb
@@ -26,7 +26,11 @@ module ActiveSupport
when nil
nil
when DATE_REGEX
- DateTime.parse(data)
+ begin
+ DateTime.parse(data)
+ rescue ArgumentError
+ data
+ end
when Array
data.map! { |d| convert_dates_from(d) }
when Hash
diff --git a/activesupport/lib/active_support/json/backends/yajl.rb b/activesupport/lib/active_support/json/backends/yajl.rb
index 64e50e0d87..58818658c7 100644
--- a/activesupport/lib/active_support/json/backends/yajl.rb
+++ b/activesupport/lib/active_support/json/backends/yajl.rb
@@ -23,7 +23,11 @@ module ActiveSupport
when nil
nil
when DATE_REGEX
- DateTime.parse(data)
+ begin
+ DateTime.parse(data)
+ rescue ArgumentError
+ data
+ end
when Array
data.map! { |d| convert_dates_from(d) }
when Hash
diff --git a/activesupport/lib/active_support/json/backends/yaml.rb b/activesupport/lib/active_support/json/backends/yaml.rb
index 4cb9d01077..e25e29d36b 100644
--- a/activesupport/lib/active_support/json/backends/yaml.rb
+++ b/activesupport/lib/active_support/json/backends/yaml.rb
@@ -7,14 +7,21 @@ module ActiveSupport
ParseError = ::StandardError
extend self
+ EXCEPTIONS = [::ArgumentError] # :nodoc:
+ begin
+ require 'psych'
+ EXCEPTIONS << Psych::SyntaxError
+ rescue LoadError
+ end
+
# Parses a JSON string or IO and converts it into an object
def decode(json)
if json.respond_to?(:read)
json = json.read
end
YAML.load(convert_json_to_yaml(json))
- rescue ArgumentError
- raise ParseError, "Invalid JSON string"
+ rescue *EXCEPTIONS => e
+ raise ParseError, "Invalid JSON string: '%s'" % json
end
protected
@@ -22,21 +29,21 @@ module ActiveSupport
def convert_json_to_yaml(json) #:nodoc:
require 'strscan' unless defined? ::StringScanner
scanner, quoting, marks, pos, times = ::StringScanner.new(json), false, [], nil, []
- while scanner.scan_until(/(\\['"]|['":,\\]|\\.)/)
+ while scanner.scan_until(/(\\['"]|['":,\\]|\\.|[\]])/)
case char = scanner[1]
when '"', "'"
if !quoting
quoting = char
pos = scanner.pos
elsif quoting == char
- if json[pos..scanner.pos-2] =~ DATE_REGEX
+ if valid_date?(json[pos..scanner.pos-2])
# found a date, track the exact positions of the quotes so we can
# overwrite them with spaces later.
- times << pos << scanner.pos
+ times << pos
end
quoting = false
end
- when ":",","
+ when ":",",", "]"
marks << scanner.pos - 1 unless quoting
when "\\"
scanner.skip(/\\/)
@@ -47,7 +54,9 @@ module ActiveSupport
json.gsub(/\\([\\\/]|u[[:xdigit:]]{4})/) do
ustr = $1
if ustr.start_with?('u')
- [ustr[1..-1].to_i(16)].pack("U")
+ char = [ustr[1..-1].to_i(16)].pack("U")
+ # "\n" needs extra escaping due to yaml formatting
+ char == "\n" ? "\\n" : char
elsif ustr == '\\'
'\\\\'
else
@@ -61,14 +70,18 @@ module ActiveSupport
left_pos.each_with_index do |left, i|
scanner.pos = left.succ
chunk = scanner.peek(right_pos[i] - scanner.pos + 1)
- # overwrite the quotes found around the dates with spaces
- while times.size > 0 && times[0] <= right_pos[i]
- chunk[times.shift - scanner.pos - 1] = ' '
+ if ActiveSupport.parse_json_times
+ # overwrite the quotes found around the dates with spaces
+ while times.size > 0 && times[0] <= right_pos[i]
+ chunk.insert(times.shift - scanner.pos - 1, '! ')
+ end
end
chunk.gsub!(/\\([\\\/]|u[[:xdigit:]]{4})/) do
ustr = $1
if ustr.start_with?('u')
- [ustr[1..-1].to_i(16)].pack("U")
+ char = [ustr[1..-1].to_i(16)].pack("U")
+ # "\n" needs extra escaping due to yaml formatting
+ char == "\n" ? "\\n" : char
elsif ustr == '\\'
'\\\\'
else
@@ -83,6 +96,16 @@ module ActiveSupport
output
end
end
+
+ private
+ def valid_date?(date_string)
+ begin
+ date_string =~ DATE_REGEX && DateTime.parse(date_string)
+ rescue ArgumentError
+ false
+ end
+ end
+
end
end
end
diff --git a/activesupport/lib/active_support/json/encoding.rb b/activesupport/lib/active_support/json/encoding.rb
index 2f9588e0f4..82b8a7e148 100644
--- a/activesupport/lib/active_support/json/encoding.rb
+++ b/activesupport/lib/active_support/json/encoding.rb
@@ -23,7 +23,7 @@ module ActiveSupport
module JSON
# matches YAML-formatted dates
- DATE_REGEX = /^(?:\d{4}-\d{2}-\d{2}|\d{4}-\d{1,2}-\d{1,2}[ \t]+\d{1,2}:\d{2}:\d{2}(\.[0-9]*)?(([ \t]*)Z|[-+]\d{2}?(:\d{2})?))$/
+ DATE_REGEX = /^(?:\d{4}-\d{2}-\d{2}|\d{4}-\d{1,2}-\d{1,2}[T \t]+\d{1,2}:\d{2}:\d{2}(\.[0-9]*)?(([ \t]*)Z|[-+]\d{2}?(:\d{2})?))$/
# Dumps object in JSON (JavaScript Object Notation). See www.json.org for more info.
def self.encode(value, options = nil)
@@ -41,9 +41,26 @@ module ActiveSupport
@seen = []
end
- def encode(value)
+ def encode(value, use_options = true)
check_for_circular_references(value) do
- value.as_json(options).encode_json(self)
+ jsonified = use_options ? value.as_json(options_for(value)) : value.as_json
+ jsonified.encode_json(self)
+ end
+ end
+
+ # like encode, but only calls as_json, without encoding to string
+ def as_json(value)
+ check_for_circular_references(value) do
+ value.as_json(options_for(value))
+ 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)
+ else
+ options
end
end
@@ -136,6 +153,12 @@ class Object
end
end
+class Struct
+ def as_json(options = nil) #:nodoc:
+ Hash[members.zip(values)]
+ end
+end
+
class TrueClass
AS_JSON = ActiveSupport::JSON::Variable.new('true').freeze
def as_json(options = nil) AS_JSON end #:nodoc:
@@ -186,13 +209,22 @@ module Enumerable
end
class Array
- def as_json(options = nil) self end #:nodoc:
- def encode_json(encoder) "[#{map { |v| encoder.encode(v) } * ','}]" end #:nodoc:
+ 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) }
+ end
+
+ def encode_json(encoder) #:nodoc:
+ # we assume here that the encoder has already run as_json on self and the elements, so we run encode_json directly
+ "[#{map { |v| v.encode_json(encoder) } * ','}]"
+ end
end
class Hash
def as_json(options = nil) #:nodoc:
- if options
+ # create a subset of the hash by applying :only or :except
+ subset = if options
if attrs = options[:only]
slice(*Array.wrap(attrs))
elsif attrs = options[:except]
@@ -203,10 +235,21 @@ class Hash
else
self
end
+
+ # 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)] }]
end
def encode_json(encoder)
- "{#{map { |k,v| "#{encoder.encode(k.to_s)}:#{encoder.encode(v)}" } * ','}}"
+ # values are encoded with use_options = false, because we don't want hash representations from ActiveModel to be
+ # processed once again with as_json with options, as this could cause unexpected results (i.e. missing fields);
+
+ # on the other hand, we need to run as_json on the elements, because the model representation may contain fields
+ # like Time/Date in their original (not jsonified) form, etc.
+
+ "{#{map { |k,v| "#{encoder.encode(k.to_s)}:#{encoder.encode(v, false)}" } * ','}}"
end
end
diff --git a/activesupport/lib/active_support/lazy_load_hooks.rb b/activesupport/lib/active_support/lazy_load_hooks.rb
index ef43fc0431..82507c1e03 100644
--- a/activesupport/lib/active_support/lazy_load_hooks.rb
+++ b/activesupport/lib/active_support/lazy_load_hooks.rb
@@ -1,21 +1,21 @@
-# lazy_load_hooks allows rails to lazily load a lot of components and thus making the app boot faster. Because of
+# 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
+# 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.
+# 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)
+# ActiveSupport.run_load_hooks(:active_record, ActiveRecord::Base)
#
module ActiveSupport
@load_hooks = Hash.new {|h,k| h[k] = [] }
diff --git a/activesupport/lib/active_support/locale/en.yml b/activesupport/lib/active_support/locale/en.yml
index 8f08f37a70..a1499bcc90 100644
--- a/activesupport/lib/active_support/locale/en.yml
+++ b/activesupport/lib/active_support/locale/en.yml
@@ -7,10 +7,10 @@ en:
default: "%Y-%m-%d"
short: "%b %d"
long: "%B %d, %Y"
-
+
day_names: [Sunday, Monday, Tuesday, Wednesday, Thursday, Friday, Saturday]
abbr_day_names: [Sun, Mon, Tue, Wed, Thu, Fri, Sat]
-
+
# Don't forget the nil at the beginning; there's no such thing as a 0th month
month_names: [~, January, February, March, April, May, June, July, August, September, October, November, December]
abbr_month_names: [~, Jan, Feb, Mar, Apr, May, Jun, Jul, Aug, Sep, Oct, Nov, Dec]
@@ -27,7 +27,7 @@ en:
long: "%B %d, %Y %H:%M"
am: "am"
pm: "pm"
-
+
# Used in array.to_sentence.
support:
array:
diff --git a/activesupport/lib/active_support/log_subscriber.rb b/activesupport/lib/active_support/log_subscriber.rb
index 83930b3f0d..10675edac5 100644
--- a/activesupport/lib/active_support/log_subscriber.rb
+++ b/activesupport/lib/active_support/log_subscriber.rb
@@ -4,7 +4,7 @@ require 'active_support/core_ext/class/attribute'
module ActiveSupport
# ActiveSupport::LogSubscriber is an object set to consume ActiveSupport::Notifications
# with solely purpose of logging. The log subscriber dispatches notifications to a
- # regirested object based on its given namespace.
+ # registered object based on its given namespace.
#
# An example would be Active Record log subscriber responsible for logging queries:
#
@@ -16,12 +16,12 @@ module ActiveSupport
# end
# end
#
- # And it's finally registed as:
+ # And it's finally registered as:
#
# ActiveRecord::LogSubscriber.attach_to :active_record
#
# Since we need to know all instance methods before attaching the log subscriber,
- # the line above should be called after your ActiveRecord::LogSubscriber definition.
+ # the line above should be called after your <tt>ActiveRecord::LogSubscriber</tt> definition.
#
# After configured, whenever a "sql.active_record" notification is published,
# it will properly dispatch the event (ActiveSupport::Notifications::Event) to
@@ -31,23 +31,10 @@ module ActiveSupport
# all logs when the request finishes (via action_dispatch.callback notification) in
# a Rails environment.
class LogSubscriber
- mattr_accessor :colorize_logging
- self.colorize_logging = true
-
- class_attribute :logger
-
- class << self
- remove_method :logger
- end
-
- def self.logger
- @logger ||= Rails.logger if defined?(Rails)
- end
-
# Embed in a String to clear all previous ANSI sequences.
CLEAR = "\e[0m"
BOLD = "\e[1m"
-
+
# Colors
BLACK = "\e[30m"
RED = "\e[31m"
@@ -58,32 +45,44 @@ module ActiveSupport
CYAN = "\e[36m"
WHITE = "\e[37m"
- def self.attach_to(namespace, log_subscriber=new, notifier=ActiveSupport::Notifications)
- log_subscribers << log_subscriber
- @@flushable_loggers = nil
+ mattr_accessor :colorize_logging
+ self.colorize_logging = true
+
+ class_attribute :logger
+
+ class << self
+ remove_method :logger
+ def logger
+ @logger ||= Rails.logger if defined?(Rails)
+ end
+
+ def attach_to(namespace, log_subscriber=new, notifier=ActiveSupport::Notifications)
+ log_subscribers << log_subscriber
+ @@flushable_loggers = nil
- log_subscriber.public_methods(false).each do |event|
- next if 'call' == event.to_s
+ log_subscriber.public_methods(false).each do |event|
+ next if 'call' == event.to_s
- notifier.subscribe("#{event}.#{namespace}", log_subscriber)
+ notifier.subscribe("#{event}.#{namespace}", log_subscriber)
+ end
end
- end
- def self.log_subscribers
- @@log_subscribers ||= []
- end
+ def log_subscribers
+ @@log_subscribers ||= []
+ end
- def self.flushable_loggers
- @@flushable_loggers ||= begin
- loggers = log_subscribers.map(&:logger)
- loggers.uniq!
- loggers.select { |l| l.respond_to?(:flush) }
+ def flushable_loggers
+ @@flushable_loggers ||= begin
+ loggers = log_subscribers.map(&:logger)
+ loggers.uniq!
+ loggers.select { |l| l.respond_to?(:flush) }
+ end
end
- end
- # Flush all log_subscribers' logger.
- def self.flush_all!
- flushable_loggers.each(&:flush)
+ # Flush all log_subscribers' logger.
+ def flush_all!
+ flushable_loggers.each(&:flush)
+ end
end
def call(message, *args)
diff --git a/activesupport/lib/active_support/log_subscriber/test_helper.rb b/activesupport/lib/active_support/log_subscriber/test_helper.rb
index 9e52cb97a9..392e33edbc 100644
--- a/activesupport/lib/active_support/log_subscriber/test_helper.rb
+++ b/activesupport/lib/active_support/log_subscriber/test_helper.rb
@@ -12,7 +12,7 @@ module ActiveSupport
# def setup
# ActiveRecord::LogSubscriber.attach_to(:active_record)
# end
- #
+ #
# def test_basic_query_logging
# Developer.all
# wait
@@ -23,11 +23,11 @@ module ActiveSupport
# end
#
# All you need to do is to ensure that your log subscriber is added to Rails::Subscriber,
- # as in the second line of the code above. The test helpers is reponsible for setting
+ # as in the second line of the code above. The test helpers are responsible for setting
# up the queue, subscriptions and turning colors in logs off.
#
# The messages are available in the @logger instance, which is a logger with limited
- # powers (it actually do not send anything to your output), and you can collect them
+ # powers (it actually does not send anything to your output), and you can collect them
# doing @logger.logged(level), where level is the level used in logging, like info,
# debug, warn and so on.
#
@@ -38,13 +38,14 @@ module ActiveSupport
ActiveSupport::LogSubscriber.colorize_logging = false
+ @old_notifier = ActiveSupport::Notifications.notifier
set_logger(@logger)
ActiveSupport::Notifications.notifier = @notifier
end
def teardown
set_logger(nil)
- ActiveSupport::Notifications.notifier = nil
+ ActiveSupport::Notifications.notifier = @old_notifier
end
class MockLogger
diff --git a/activesupport/lib/active_support/memoizable.rb b/activesupport/lib/active_support/memoizable.rb
index 9fb506fea1..0a7bcd5bb8 100644
--- a/activesupport/lib/active_support/memoizable.rb
+++ b/activesupport/lib/active_support/memoizable.rb
@@ -95,6 +95,8 @@ module ActiveSupport
#
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
diff --git a/activesupport/lib/active_support/message_encryptor.rb b/activesupport/lib/active_support/message_encryptor.rb
index 51fa626b45..4f7cd12d48 100644
--- a/activesupport/lib/active_support/message_encryptor.rb
+++ b/activesupport/lib/active_support/message_encryptor.rb
@@ -2,12 +2,12 @@ require 'openssl'
require 'active_support/base64'
module ActiveSupport
- # MessageEncryptor is a simple way to encrypt values which get stored somewhere
+ # MessageEncryptor is a simple way to encrypt values which get stored somewhere
# you don't trust.
- #
+ #
# The cipher text and initialization vector are base64 encoded and returned to you.
#
- # This can be used in situations similar to the MessageVerifier, but where you don't
+ # 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.
class MessageEncryptor
class InvalidMessage < StandardError; end
@@ -17,53 +17,53 @@ module ActiveSupport
@secret = secret
@cipher = cipher
end
-
+
def encrypt(value)
cipher = new_cipher
# Rely on OpenSSL for the initialization vector
iv = cipher.random_iv
-
- cipher.encrypt
+
+ cipher.encrypt
cipher.key = @secret
cipher.iv = iv
-
- encrypted_data = cipher.update(Marshal.dump(value))
+
+ encrypted_data = cipher.update(Marshal.dump(value))
encrypted_data << cipher.final
[encrypted_data, iv].map {|v| ActiveSupport::Base64.encode64s(v)}.join("--")
end
-
+
def decrypt(encrypted_message)
cipher = new_cipher
encrypted_data, iv = encrypted_message.split("--").map {|v| ActiveSupport::Base64.decode64(v)}
-
+
cipher.decrypt
cipher.key = @secret
cipher.iv = iv
decrypted_data = cipher.update(encrypted_data)
decrypted_data << cipher.final
-
+
Marshal.load(decrypted_data)
rescue OpenSSLCipherError, TypeError
raise InvalidMessage
end
-
+
def encrypt_and_sign(value)
verifier.generate(encrypt(value))
end
-
+
def decrypt_and_verify(value)
decrypt(verifier.verify(value))
end
-
-
-
- private
+
+
+
+ private
def new_cipher
OpenSSL::Cipher::Cipher.new(@cipher)
end
-
+
def verifier
MessageVerifier.new(@secret)
end
diff --git a/activesupport/lib/active_support/message_verifier.rb b/activesupport/lib/active_support/message_verifier.rb
index 6c46b68eaf..8f3946325a 100644
--- a/activesupport/lib/active_support/message_verifier.rb
+++ b/activesupport/lib/active_support/message_verifier.rb
@@ -2,30 +2,30 @@ require 'active_support/base64'
require 'active_support/core_ext/object/blank'
module ActiveSupport
- # MessageVerifier makes it easy to generate and verify messages which are signed
+ # +MessageVerifier+ makes it easy to generate and verify messages which are signed
# to prevent tampering.
- #
+ #
# This is useful for cases like remember-me tokens and auto-unsubscribe links where the
# session store isn't suitable or available.
#
# Remember Me:
# cookies[:remember_me] = @verifier.generate([@user.id, 2.weeks.from_now])
- #
+ #
# In the authentication filter:
#
# id, time = @verifier.verify(cookies[:remember_me])
# if time < Time.now
# self.current_user = User.find(id)
# end
- #
+ #
class MessageVerifier
class InvalidSignature < StandardError; end
-
+
def initialize(secret, digest = 'SHA1')
@secret = secret
@digest = digest
end
-
+
def verify(signed_message)
raise InvalidSignature if signed_message.blank?
@@ -36,12 +36,12 @@ module ActiveSupport
raise InvalidSignature
end
end
-
+
def generate(value)
data = ActiveSupport::Base64.encode64s(Marshal.dump(value))
"#{data}--#{generate_digest(data)}"
end
-
+
private
# constant-time comparison algorithm to prevent timing attacks
def secure_compare(a, b)
diff --git a/activesupport/lib/active_support/multibyte.rb b/activesupport/lib/active_support/multibyte.rb
index 8ffdf5a1bf..57e8e24bf4 100644
--- a/activesupport/lib/active_support/multibyte.rb
+++ b/activesupport/lib/active_support/multibyte.rb
@@ -6,7 +6,7 @@ module ActiveSupport #:nodoc:
autoload :EncodingError, 'active_support/multibyte/exceptions'
autoload :Chars, 'active_support/multibyte/chars'
autoload :Unicode, 'active_support/multibyte/unicode'
-
+
# The proxy class returned when calling mb_chars. You can use this accessor to configure your own proxy
# class so you can support other encodings. See the ActiveSupport::Multibyte::Chars implementation for
# an example how to do this.
diff --git a/activesupport/lib/active_support/multibyte/chars.rb b/activesupport/lib/active_support/multibyte/chars.rb
index 019fb2df06..f9607f1aaf 100644
--- a/activesupport/lib/active_support/multibyte/chars.rb
+++ b/activesupport/lib/active_support/multibyte/chars.rb
@@ -64,7 +64,7 @@ module ActiveSupport #:nodoc:
# Returns +true+ if _obj_ responds to the given method. Private methods are included in the search
# only if the optional second parameter evaluates to +true+.
def respond_to?(method, include_private=false)
- super || @wrapped_string.respond_to?(method, include_private) || false
+ super || @wrapped_string.respond_to?(method, include_private)
end
# Enable more predictable duck-typing on String-like classes. See Object#acts_like?.
@@ -270,13 +270,14 @@ module ActiveSupport #:nodoc:
@wrapped_string[*args] = replace_by
else
result = Unicode.u_unpack(@wrapped_string)
- if args[0].is_a?(Fixnum)
+ 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)
- elsif args.first.is_a?(Range)
+ when Range
raise RangeError, "#{args[0]} out of range" if args[0].min >= result.length
range = args[0]
else
@@ -437,7 +438,7 @@ module ActiveSupport #:nodoc:
begin
@wrapped_string[0...byte_offset].unpack('U*').length
- rescue ArgumentError => e
+ rescue ArgumentError
byte_offset -= 1
retry
end
diff --git a/activesupport/lib/active_support/multibyte/unicode.rb b/activesupport/lib/active_support/multibyte/unicode.rb
index 1139783b65..513f83e445 100644
--- a/activesupport/lib/active_support/multibyte/unicode.rb
+++ b/activesupport/lib/active_support/multibyte/unicode.rb
@@ -247,7 +247,7 @@ module ActiveSupport
if is_unused || is_restricted
bytes[i] = tidy_byte(byte)
elsif is_cont
- # Not expecting contination byte? Clean up. Otherwise, now expect one less.
+ # Not expecting continuation byte? Clean up. Otherwise, now expect one less.
conts_expected == 0 ? bytes[i] = tidy_byte(byte) : conts_expected -= 1
else
if conts_expected > 0
diff --git a/activesupport/lib/active_support/notifications.rb b/activesupport/lib/active_support/notifications.rb
index fd79188ba4..77696eb1db 100644
--- a/activesupport/lib/active_support/notifications.rb
+++ b/activesupport/lib/active_support/notifications.rb
@@ -44,8 +44,11 @@ module ActiveSupport
@instrumenters = Hash.new { |h,k| h[k] = notifier.listening?(k) }
class << self
- attr_writer :notifier
- delegate :publish, :to => :notifier
+ attr_accessor :notifier
+
+ def publish(name, *args)
+ notifier.publish(name, *args)
+ end
def instrument(name, payload = {})
if @instrumenters[name]
@@ -61,18 +64,16 @@ module ActiveSupport
end
end
- def unsubscribe(*args)
- notifier.unsubscribe(*args)
+ def unsubscribe(args)
+ notifier.unsubscribe(args)
@instrumenters.clear
end
- def notifier
- @notifier ||= Fanout.new
- end
-
def instrumenter
Thread.current[:"instrumentation_#{notifier.object_id}"] ||= Instrumenter.new(notifier)
end
end
+
+ self.notifier = Fanout.new
end
end
diff --git a/activesupport/lib/active_support/ordered_hash.rb b/activesupport/lib/active_support/ordered_hash.rb
index 2e8d538d0b..fbc40d1b69 100644
--- a/activesupport/lib/active_support/ordered_hash.rb
+++ b/activesupport/lib/active_support/ordered_hash.rb
@@ -1,3 +1,8 @@
+begin
+ require 'psych'
+rescue LoadError
+end
+
require 'yaml'
YAML.add_builtin_type("omap") do |type, val|
@@ -8,21 +13,29 @@ module ActiveSupport
# The order of iteration over hashes in Ruby 1.8 is undefined. For example, you do not know the
# order in which +keys+ will return keys, or +each+ yield pairs. <tt>ActiveSupport::OrderedHash</tt>
# implements a hash that preserves insertion order, as in Ruby 1.9:
- #
+ #
# oh = ActiveSupport::OrderedHash.new
# oh[:a] = 1
# oh[:b] = 2
# oh.keys # => [:a, :b], this order is guaranteed
- #
+ #
# <tt>ActiveSupport::OrderedHash</tt> is namespaced to prevent conflicts with other implementations.
class OrderedHash < ::Hash #:nodoc:
def to_yaml_type
"!tag:yaml.org,2002:omap"
end
+ def encode_with(coder)
+ 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, to_yaml_style) do |seq|
+ out.seq(taguri) do |seq|
each do |k, v|
seq.add(k => v)
end
@@ -79,7 +92,7 @@ module ActiveSupport
end
def []=(key, value)
- @keys << key if !has_key?(key)
+ @keys << key unless has_key?(key)
super
end
@@ -124,19 +137,27 @@ module ActiveSupport
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
alias_method :each_pair, :each
+ alias_method :select, :find_all
+
def clear
super
@keys.clear
diff --git a/activesupport/lib/active_support/ordered_options.rb b/activesupport/lib/active_support/ordered_options.rb
index 7fc2b45b51..8d8e6ebc58 100644
--- a/activesupport/lib/active_support/ordered_options.rb
+++ b/activesupport/lib/active_support/ordered_options.rb
@@ -1,23 +1,26 @@
require 'active_support/ordered_hash'
# Usually key value pairs are handled something like this:
-#
-# h = ActiveSupport::OrderedOptions.new
+#
+# h = {}
# h[:boy] = 'John'
# h[:girl] = 'Mary'
# h[:boy] # => 'John'
# h[:girl] # => 'Mary'
-#
-# Using <tt>OrderedOptions</tt> above code could be reduced to:
+#
+# Using <tt>OrderedOptions</tt>, the above code could be reduced to:
#
# h = ActiveSupport::OrderedOptions.new
# h.boy = 'John'
# h.girl = 'Mary'
# h.boy # => 'John'
# h.girl # => 'Mary'
-#
+#
module ActiveSupport #:nodoc:
class OrderedOptions < OrderedHash
+ alias_method :_get, :[] # preserve the original #[] method
+ protected :_get # make it protected
+
def []=(key, value)
super(key.to_sym, value)
end
@@ -28,7 +31,7 @@ module ActiveSupport #:nodoc:
def method_missing(name, *args)
if name.to_s =~ /(.*)=$/
- self[$1.to_sym] = args.first
+ self[$1] = args.first
else
self[name]
end
@@ -36,8 +39,19 @@ module ActiveSupport #:nodoc:
end
class InheritableOptions < OrderedOptions
- def initialize(parent)
- super() { |h,k| parent[k] }
+ def initialize(parent = nil)
+ if parent.kind_of?(OrderedOptions)
+ # use the faster _get when dealing with OrderedOptions
+ super() { |h,k| parent._get(k) }
+ elsif parent
+ super() { |h,k| parent[k] }
+ else
+ super()
+ end
+ end
+
+ def inheritable_copy
+ self.class.new(self)
end
end
end
diff --git a/activesupport/lib/active_support/rescuable.rb b/activesupport/lib/active_support/rescuable.rb
index cd6f92cdfe..0f4a06468a 100644
--- a/activesupport/lib/active_support/rescuable.rb
+++ b/activesupport/lib/active_support/rescuable.rb
@@ -47,6 +47,7 @@ module ActiveSupport
# exception.record.new_record? ? ...
# end
# end
+ #
def rescue_from(*klasses, &block)
options = klasses.extract_options!
diff --git a/activesupport/lib/active_support/secure_random.rb b/activesupport/lib/active_support/secure_random.rb
index 73344498cb..368954907f 100644
--- a/activesupport/lib/active_support/secure_random.rb
+++ b/activesupport/lib/active_support/secure_random.rb
@@ -46,8 +46,10 @@ module ActiveSupport
# p SecureRandom.random_bytes(10) # => "\016\t{\370g\310pbr\301"
# p SecureRandom.random_bytes(10) # => "\323U\030TO\234\357\020\a\337"
# ...
+ #
module SecureRandom
- # SecureRandom.random_bytes generates a random binary string.
+
+ # Generates a random binary string.
#
# The argument n specifies the length of the result string.
#
@@ -56,6 +58,7 @@ module ActiveSupport
#
# If secure random number generator is not available,
# NotImplementedError is raised.
+ #
def self.random_bytes(n=nil)
n ||= 16
@@ -92,7 +95,7 @@ module ActiveSupport
end
end
- if !defined?(@has_win32)
+ unless defined?(@has_win32)
begin
require 'Win32API'
@@ -124,7 +127,7 @@ module ActiveSupport
raise NotImplementedError, "No random device"
end
- # SecureRandom.hex generates a random hex string.
+ # Generates a random hex string.
#
# The argument n specifies the length of the random length.
# The length of the result string is twice of n.
@@ -134,11 +137,12 @@ module ActiveSupport
#
# If secure random number generator is not available,
# NotImplementedError is raised.
+ #
def self.hex(n=nil)
random_bytes(n).unpack("H*")[0]
end
- # SecureRandom.base64 generates a random base64 string.
+ # Generates a random base64 string.
#
# The argument n specifies the length of the random length.
# The length of the result string is about 4/3 of n.
@@ -148,11 +152,12 @@ module ActiveSupport
#
# If secure random number generator is not available,
# NotImplementedError is raised.
+ #
def self.base64(n=nil)
[random_bytes(n)].pack("m*").delete("\n")
end
- # SecureRandom.random_number generates a random number.
+ # Generates a random number.
#
# If an positive integer is given as n,
# SecureRandom.random_number returns an integer:
@@ -161,6 +166,7 @@ module ActiveSupport
# If 0 is given or an argument is not given,
# SecureRandom.random_number returns an float:
# 0.0 <= SecureRandom.random_number() < 1.0.
+ #
def self.random_number(n=0)
if 0 < n
hex = n.to_s(16)
diff --git a/activesupport/lib/active_support/test_case.rb b/activesupport/lib/active_support/test_case.rb
index ed8c02ba3e..fb52fc7083 100644
--- a/activesupport/lib/active_support/test_case.rb
+++ b/activesupport/lib/active_support/test_case.rb
@@ -24,8 +24,7 @@ module ActiveSupport
else
Assertion = Test::Unit::AssertionFailedError
- require 'active_support/testing/default'
- include ActiveSupport::Testing::Default
+ undef :default_test
end
$tags = {}
diff --git a/activesupport/lib/active_support/testing/assertions.rb b/activesupport/lib/active_support/testing/assertions.rb
index 33793f0688..a4e0361a32 100644
--- a/activesupport/lib/active_support/testing/assertions.rb
+++ b/activesupport/lib/active_support/testing/assertions.rb
@@ -63,19 +63,21 @@ module ActiveSupport
def assert_no_difference(expression, message = nil, &block)
assert_difference expression, 0, message, &block
end
-
+
# Test if an expression is blank. Passes if object.blank? is true.
#
# assert_blank [] # => true
- def assert_blank(object)
- assert object.blank?, "#{object.inspect} is not blank"
+ def assert_blank(object, message=nil)
+ message ||= "#{object.inspect} is not blank"
+ assert object.blank?, message
end
-
+
# Test if an expression is not blank. Passes if object.present? is true.
#
# assert_present {:data => 'x' } # => true
- def assert_present(object)
- assert object.present?, "#{object.inspect} is blank"
+ def assert_present(object, message=nil)
+ message ||= "#{object.inspect} is blank"
+ assert object.present?, message
end
end
end
diff --git a/activesupport/lib/active_support/testing/declarative.rb b/activesupport/lib/active_support/testing/declarative.rb
index 70a6c2ca60..1c05d45888 100644
--- a/activesupport/lib/active_support/testing/declarative.rb
+++ b/activesupport/lib/active_support/testing/declarative.rb
@@ -1,10 +1,10 @@
module ActiveSupport
module Testing
module Declarative
-
+
def self.extended(klass)
klass.class_eval do
-
+
unless method_defined?(:describe)
def self.describe(text)
class_eval <<-RUBY_EVAL, __FILE__, __LINE__ + 1
@@ -14,9 +14,9 @@ module ActiveSupport
RUBY_EVAL
end
end
-
+
end
- end
+ end
unless defined?(Spec)
# test "verify something" do
diff --git a/activesupport/lib/active_support/testing/default.rb b/activesupport/lib/active_support/testing/default.rb
deleted file mode 100644
index a0bd6303c7..0000000000
--- a/activesupport/lib/active_support/testing/default.rb
+++ /dev/null
@@ -1,9 +0,0 @@
-module ActiveSupport
- module Testing
- module Default #:nodoc:
- # Placeholder so test/unit ignores test cases without any tests.
- def default_test
- end
- end
- end
-end
diff --git a/activesupport/lib/active_support/testing/isolation.rb b/activesupport/lib/active_support/testing/isolation.rb
index d629f6f2b7..6b29ba4c10 100644
--- a/activesupport/lib/active_support/testing/isolation.rb
+++ b/activesupport/lib/active_support/testing/isolation.rb
@@ -34,7 +34,7 @@ module ActiveSupport
module Isolation
def self.forking_env?
- !ENV["NO_FORK"] && ((Config::CONFIG['host_os'] !~ /mswin|mingw/) && (RUBY_PLATFORM !~ /java/))
+ !ENV["NO_FORK"] && ((RbConfig::CONFIG['host_os'] !~ /mswin|mingw/) && (RUBY_PLATFORM !~ /java/))
end
def self.included(base)
diff --git a/activesupport/lib/active_support/testing/pending.rb b/activesupport/lib/active_support/testing/pending.rb
index 21134ff9e2..3d119e2fba 100644
--- a/activesupport/lib/active_support/testing/pending.rb
+++ b/activesupport/lib/active_support/testing/pending.rb
@@ -1,5 +1,5 @@
# Some code from jeremymcanally's "pending"
-# http://github.com/jeremymcanally/pending/tree/master
+# https://github.com/jeremymcanally/pending/tree/master
module ActiveSupport
module Testing
@@ -25,13 +25,13 @@ module ActiveSupport
failed = true
end
- flunk("<#{description}> did not fail.") unless failed
+ 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:"
@@ -42,7 +42,7 @@ module ActiveSupport
end
end
end
-
+
end
end
-end \ No newline at end of file
+end
diff --git a/activesupport/lib/active_support/testing/performance.rb b/activesupport/lib/active_support/testing/performance.rb
index f7ddf6421d..8c91a061fb 100644
--- a/activesupport/lib/active_support/testing/performance.rb
+++ b/activesupport/lib/active_support/testing/performance.rb
@@ -58,16 +58,16 @@ begin
metric.send(mode) { __send__ @method_name }
rescue ::Test::Unit::AssertionFailedError => e
add_failure(e.message, e.backtrace)
- rescue StandardError, ScriptError
- add_error($!)
+ rescue StandardError, ScriptError => e
+ add_error(e)
ensure
begin
teardown
run_callbacks :teardown, :enumerator => :reverse_each
rescue ::Test::Unit::AssertionFailedError => e
add_failure(e.message, e.backtrace)
- rescue StandardError, ScriptError
- add_error($!)
+ rescue StandardError, ScriptError => e
+ add_error(e)
end
end
@@ -401,7 +401,7 @@ begin
Mode = RubyProf::GC_TIME if RubyProf.const_defined?(:GC_TIME)
# Ruby 1.9 with GC::Profiler
- if GC.respond_to?(:total_time)
+ if defined?(GC::Profiler) && GC::Profiler.respond_to?(:total_time)
def measure
GC::Profiler.total_time
end
diff --git a/activesupport/lib/active_support/testing/setup_and_teardown.rb b/activesupport/lib/active_support/testing/setup_and_teardown.rb
index b2d9ddfeb7..22e41fa905 100644
--- a/activesupport/lib/active_support/testing/setup_and_teardown.rb
+++ b/activesupport/lib/active_support/testing/setup_and_teardown.rb
@@ -31,14 +31,14 @@ module ActiveSupport
def run(runner)
result = '.'
begin
- _run_setup_callbacks do
+ run_callbacks :setup do
result = super
end
rescue Exception => e
result = runner.puke(self.class, method_name, e)
ensure
begin
- _run_teardown_callbacks
+ run_callbacks :teardown
rescue Exception => e
result = runner.puke(self.class, method_name, e)
end
@@ -62,7 +62,7 @@ module ActiveSupport
begin
begin
- _run_setup_callbacks do
+ run_callbacks :setup do
setup
__send__(@method_name)
mocha_verify(mocha_counter) if mocha_counter
@@ -77,7 +77,7 @@ module ActiveSupport
ensure
begin
teardown
- _run_teardown_callbacks
+ run_callbacks :teardown
rescue Test::Unit::AssertionFailedError => e
add_failure(e.message, e.backtrace)
rescue Exception => e
diff --git a/activesupport/lib/active_support/time_with_zone.rb b/activesupport/lib/active_support/time_with_zone.rb
index ad6c3de1f5..c66aa78ce8 100644
--- a/activesupport/lib/active_support/time_with_zone.rb
+++ b/activesupport/lib/active_support/time_with_zone.rb
@@ -18,7 +18,7 @@ module ActiveSupport
#
# See Time and TimeZone for further documentation of these methods.
#
- # TimeWithZone instances implement the same API as Ruby Time instances, so that Time and TimeWithZone instances are interchangeable.
+ # TimeWithZone instances implement the same API as Ruby Time instances, so that Time and TimeWithZone instances are interchangeable.
# Examples:
#
# t = Time.zone.now # => Sun, 18 May 2008 13:27:25 EDT -04:00
@@ -32,11 +32,12 @@ module ActiveSupport
# t > Time.utc(1999) # => true
# t.is_a?(Time) # => true
# t.is_a?(ActiveSupport::TimeWithZone) # => true
+ #
class TimeWithZone
def self.name
'Time' # Report class name as 'Time' to thwart type checking
end
-
+
include Comparable
attr_reader :time_zone
@@ -72,7 +73,7 @@ module ActiveSupport
# Returns a <tt>Time.local()</tt> instance of the simultaneous time in your system's <tt>ENV['TZ']</tt> zone
def localtime
- utc.getlocal
+ utc.respond_to?(:getlocal) ? utc.getlocal : utc.to_time.getlocal
end
alias_method :getlocal, :localtime
@@ -127,6 +128,7 @@ module ActiveSupport
# # With ActiveSupport::JSON::Encoding.use_standard_json_time_format = false
# Time.utc(2005,2,1,15,15,10).in_time_zone.to_json
# # => "2005/02/01 15:15:10 +0000"
+ #
def as_json(options = nil)
if ActiveSupport::JSON::Encoding.use_standard_json_time_format
xmlschema
@@ -135,14 +137,20 @@ module ActiveSupport
end
end
- def to_yaml(options = {})
- if options.kind_of?(YAML::Emitter)
- utc.to_yaml(options)
+ def encode_with(coder)
+ if coder.respond_to?(:represent_object)
+ coder.represent_object(nil, utc)
else
- time.to_yaml(options).gsub('Z', formatted_offset(true, 'Z'))
+ coder.represent_scalar(nil, utc.strftime("%Y-%m-%d %H:%M:%S.%9NZ"))
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
@@ -273,7 +281,7 @@ module ActiveSupport
# A TimeWithZone acts like a Time, so just return +self+.
def to_time
- self
+ utc
end
def to_datetime
diff --git a/activesupport/lib/active_support/version.rb b/activesupport/lib/active_support/version.rb
index 9d2cf13260..690fc7f0fc 100644
--- a/activesupport/lib/active_support/version.rb
+++ b/activesupport/lib/active_support/version.rb
@@ -1,10 +1,10 @@
module ActiveSupport
module VERSION #:nodoc:
MAJOR = 3
- MINOR = 0
+ MINOR = 1
TINY = 0
- BUILD = "rc"
+ PRE = "beta"
- STRING = [MAJOR, MINOR, TINY, BUILD].join('.')
+ STRING = [MAJOR, MINOR, TINY, PRE].compact.join('.')
end
end
diff --git a/activesupport/lib/active_support/whiny_nil.rb b/activesupport/lib/active_support/whiny_nil.rb
index 91ddef2619..bcedbfd57a 100644
--- a/activesupport/lib/active_support/whiny_nil.rb
+++ b/activesupport/lib/active_support/whiny_nil.rb
@@ -37,7 +37,7 @@ class NilClass
# Raises a RuntimeError when you attempt to call +id+ on +nil+.
def id
- raise RuntimeError, "Called id for nil, which would mistakenly be 4 -- if you really wanted the id of nil, use object_id", caller
+ 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
diff --git a/activesupport/lib/active_support/xml_mini.rb b/activesupport/lib/active_support/xml_mini.rb
index 352172027b..cddfcddb57 100644
--- a/activesupport/lib/active_support/xml_mini.rb
+++ b/activesupport/lib/active_support/xml_mini.rb
@@ -25,7 +25,7 @@ module ActiveSupport
DEFAULT_ENCODINGS = {
"binary" => "base64"
- } unless defined?(TYPE_NAMES)
+ } unless defined?(DEFAULT_ENCODINGS)
TYPE_NAMES = {
"Symbol" => "symbol",
@@ -126,9 +126,11 @@ module ActiveSupport
end
def rename_key(key, options = {})
- camelize = options.has_key?(:camelize) && options[:camelize]
+ camelize = options[:camelize]
dasherize = !options.has_key?(:dasherize) || options[:dasherize]
- key = key.camelize if camelize
+ if camelize
+ key = true == camelize ? key.camelize : key.camelize(camelize)
+ end
key = _dasherize(key) if dasherize
key
end
diff --git a/activesupport/lib/active_support/xml_mini/libxml.rb b/activesupport/lib/active_support/xml_mini/libxml.rb
index 7fdcb11465..16570c6aea 100644
--- a/activesupport/lib/active_support/xml_mini/libxml.rb
+++ b/activesupport/lib/active_support/xml_mini/libxml.rb
@@ -1,5 +1,6 @@
require 'libxml'
require 'active_support/core_ext/object/blank'
+require 'stringio'
# = XmlMini LibXML implementation
module ActiveSupport
diff --git a/activesupport/lib/active_support/xml_mini/libxmlsax.rb b/activesupport/lib/active_support/xml_mini/libxmlsax.rb
index fe2c1b9349..2536b1f33e 100644
--- a/activesupport/lib/active_support/xml_mini/libxmlsax.rb
+++ b/activesupport/lib/active_support/xml_mini/libxmlsax.rb
@@ -1,5 +1,6 @@
require 'libxml'
require 'active_support/core_ext/object/blank'
+require 'stringio'
# = XmlMini LibXML implementation using a SAX-based parser
module ActiveSupport
@@ -82,4 +83,4 @@ module ActiveSupport
end
end
end
-end \ No newline at end of file
+end
diff --git a/activesupport/lib/active_support/xml_mini/nokogiri.rb b/activesupport/lib/active_support/xml_mini/nokogiri.rb
index e03a744257..04ec9e8ab8 100644
--- a/activesupport/lib/active_support/xml_mini/nokogiri.rb
+++ b/activesupport/lib/active_support/xml_mini/nokogiri.rb
@@ -5,6 +5,7 @@ rescue LoadError => e
raise e
end
require 'active_support/core_ext/object/blank'
+require 'stringio'
# = XmlMini Nokogiri implementation
module ActiveSupport
diff --git a/activesupport/lib/active_support/xml_mini/nokogirisax.rb b/activesupport/lib/active_support/xml_mini/nokogirisax.rb
index 38c8685390..93fd3dfe57 100644
--- a/activesupport/lib/active_support/xml_mini/nokogirisax.rb
+++ b/activesupport/lib/active_support/xml_mini/nokogirisax.rb
@@ -5,6 +5,7 @@ rescue LoadError => e
raise e
end
require 'active_support/core_ext/object/blank'
+require 'stringio'
# = XmlMini Nokogiri implementation using a SAX-based parser
module ActiveSupport
@@ -38,8 +39,7 @@ module ActiveSupport
end
def start_element(name, attrs = [])
- new_hash = { CONTENT_KEY => '' }
- new_hash[attrs.shift] = attrs.shift while attrs.length > 0
+ new_hash = { CONTENT_KEY => '' }.merge(Hash[attrs])
new_hash[HASH_SIZE_KEY] = new_hash.size + 1
case current_hash[name]
diff --git a/activesupport/lib/active_support/xml_mini/rexml.rb b/activesupport/lib/active_support/xml_mini/rexml.rb
index a58f22ee5d..a13ad10118 100644
--- a/activesupport/lib/active_support/xml_mini/rexml.rb
+++ b/activesupport/lib/active_support/xml_mini/rexml.rb
@@ -1,5 +1,6 @@
require 'active_support/core_ext/kernel/reporting'
require 'active_support/core_ext/object/blank'
+require 'stringio'
# = XmlMini ReXML implementation
module ActiveSupport
@@ -19,7 +20,7 @@ module ActiveSupport
if !data.respond_to?(:read)
data = StringIO.new(data || '')
end
-
+
char = data.getc
if char.nil?
{}