aboutsummaryrefslogtreecommitdiffstats
path: root/activesupport/lib/active_support/core_ext
diff options
context:
space:
mode:
authorOlli Jokinen <olli.jokinen@enemy.fi>2011-12-01 15:32:59 +0200
committerOlli Jokinen <olli.jokinen@enemy.fi>2011-12-01 15:32:59 +0200
commitb4e1903d23a760028d58bc3bb20a1d491bfd4a4b (patch)
treea40bdce1bd4800124ab6eaed2a6be017bf9cfd3d /activesupport/lib/active_support/core_ext
parentfae9ad9c712decef70b379f5aa1faa0149902831 (diff)
parent1e51cd957e3c90f4be35f1f0c4c380d8f7d40d66 (diff)
downloadrails-b4e1903d23a760028d58bc3bb20a1d491bfd4a4b.tar.gz
rails-b4e1903d23a760028d58bc3bb20a1d491bfd4a4b.tar.bz2
rails-b4e1903d23a760028d58bc3bb20a1d491bfd4a4b.zip
Merge remote-tracking branch 'upstream/master'
Diffstat (limited to 'activesupport/lib/active_support/core_ext')
-rw-r--r--activesupport/lib/active_support/core_ext/array.rb1
-rw-r--r--activesupport/lib/active_support/core_ext/array/conversions.rb6
-rw-r--r--activesupport/lib/active_support/core_ext/array/prepend_and_append.rb7
-rw-r--r--activesupport/lib/active_support/core_ext/big_decimal/conversions.rb7
-rw-r--r--activesupport/lib/active_support/core_ext/date/calculations.rb49
-rw-r--r--activesupport/lib/active_support/core_ext/enumerable.rb4
-rw-r--r--activesupport/lib/active_support/core_ext/hash/indifferent_access.rb2
-rw-r--r--activesupport/lib/active_support/core_ext/hash/slice.rb2
-rw-r--r--activesupport/lib/active_support/core_ext/io.rb15
-rw-r--r--activesupport/lib/active_support/core_ext/module.rb3
-rw-r--r--activesupport/lib/active_support/core_ext/module/delegation.rb53
-rw-r--r--activesupport/lib/active_support/core_ext/module/qualified_const.rb64
-rw-r--r--activesupport/lib/active_support/core_ext/module/reachable.rb4
-rw-r--r--activesupport/lib/active_support/core_ext/module/remove_method.rb9
-rw-r--r--activesupport/lib/active_support/core_ext/module/synchronization.rb2
-rw-r--r--activesupport/lib/active_support/core_ext/object/inclusion.rb24
-rw-r--r--activesupport/lib/active_support/core_ext/object/to_json.rb4
-rw-r--r--activesupport/lib/active_support/core_ext/object/to_query.rb2
-rw-r--r--activesupport/lib/active_support/core_ext/object/try.rb2
-rw-r--r--activesupport/lib/active_support/core_ext/range/conversions.rb2
-rw-r--r--activesupport/lib/active_support/core_ext/string/inflections.rb49
-rw-r--r--activesupport/lib/active_support/core_ext/string/output_safety.rb32
-rw-r--r--activesupport/lib/active_support/core_ext/time/calculations.rb54
-rw-r--r--activesupport/lib/active_support/core_ext/time/conversions.rb22
24 files changed, 329 insertions, 90 deletions
diff --git a/activesupport/lib/active_support/core_ext/array.rb b/activesupport/lib/active_support/core_ext/array.rb
index 4688468a8f..268c9bed4c 100644
--- a/activesupport/lib/active_support/core_ext/array.rb
+++ b/activesupport/lib/active_support/core_ext/array.rb
@@ -5,3 +5,4 @@ require 'active_support/core_ext/array/conversions'
require 'active_support/core_ext/array/extract_options'
require 'active_support/core_ext/array/grouping'
require 'active_support/core_ext/array/random_access'
+require 'active_support/core_ext/array/prepend_and_append'
diff --git a/activesupport/lib/active_support/core_ext/array/conversions.rb b/activesupport/lib/active_support/core_ext/array/conversions.rb
index 3b22e8b4f9..f3d06ecb2f 100644
--- a/activesupport/lib/active_support/core_ext/array/conversions.rb
+++ b/activesupport/lib/active_support/core_ext/array/conversions.rb
@@ -39,10 +39,10 @@ class Array
#
# Blog.all.to_formatted_s # => "First PostSecond PostThird Post"
#
- # Adding in the <tt>:db</tt> argument as the format yields a prettier
- # output:
+ # Adding in the <tt>:db</tt> argument as the format yields a comma separated
+ # id list:
#
- # Blog.all.to_formatted_s(:db) # => "First Post,Second Post,Third Post"
+ # Blog.all.to_formatted_s(:db) # => "1,2,3"
def to_formatted_s(format = :default)
case format
when :db
diff --git a/activesupport/lib/active_support/core_ext/array/prepend_and_append.rb b/activesupport/lib/active_support/core_ext/array/prepend_and_append.rb
new file mode 100644
index 0000000000..27718f19d4
--- /dev/null
+++ b/activesupport/lib/active_support/core_ext/array/prepend_and_append.rb
@@ -0,0 +1,7 @@
+class Array
+ # The human way of thinking about adding stuff to the end of a list is with append
+ alias_method :append, :<<
+
+ # The human way of thinking about adding stuff to the beginning of a list is with prepend
+ alias_method :prepend, :unshift
+end \ No newline at end of file
diff --git a/activesupport/lib/active_support/core_ext/big_decimal/conversions.rb b/activesupport/lib/active_support/core_ext/big_decimal/conversions.rb
index 080604147d..391bdc925d 100644
--- a/activesupport/lib/active_support/core_ext/big_decimal/conversions.rb
+++ b/activesupport/lib/active_support/core_ext/big_decimal/conversions.rb
@@ -29,8 +29,11 @@ class BigDecimal
coder.represent_scalar(nil, YAML_MAPPING[string] || string)
end
- def to_d
- self
+ # Backport this method if it doesn't exist
+ unless method_defined?(:to_d)
+ def to_d
+ self
+ end
end
DEFAULT_STRING_FORMAT = 'F'
diff --git a/activesupport/lib/active_support/core_ext/date/calculations.rb b/activesupport/lib/active_support/core_ext/date/calculations.rb
index 26a99658cc..f0f67765c6 100644
--- a/activesupport/lib/active_support/core_ext/date/calculations.rb
+++ b/activesupport/lib/active_support/core_ext/date/calculations.rb
@@ -174,31 +174,54 @@ class Date
months_since(1)
end unless method_defined?(:next_month)
- # Returns a new Date/DateTime representing the "start" of this week (i.e, Monday; DateTime objects will have time set to 0:00).
- def beginning_of_week
- days_to_monday = self.wday!=0 ? self.wday-1 : 6
- result = self - days_to_monday
- self.acts_like?(:time) ? result.midnight : result
+ # Returns number of days to start of this week. Week is assumed to start on
+ # +start_day+, default is +:monday+.
+ def days_to_week_start(start_day = :monday)
+ start_day_number = DAYS_INTO_WEEK[start_day]
+ current_day_number = wday != 0 ? wday - 1 : 6
+ (current_day_number - start_day_number) % 7
+ end
+
+ # Returns a new +Date+/+DateTime+ representing the start of this week. Week is
+ # assumed to start on +start_day+, default is +:monday+. +DateTime+ objects
+ # have their time set to 0:00.
+ def beginning_of_week(start_day = :monday)
+ days_to_start = days_to_week_start(start_day)
+ result = self - days_to_start
+ acts_like?(:time) ? result.midnight : result
end
- alias :monday :beginning_of_week
alias :at_beginning_of_week :beginning_of_week
- # Returns a new Date/DateTime representing the end of this week (Sunday, DateTime objects will have time set to 23:59:59).
- def end_of_week
- days_to_sunday = self.wday!=0 ? 7-self.wday : 0
- result = self + days_to_sunday.days
+ # Returns a new +Date+/+DateTime+ representing the start of this week. Week is
+ # assumed to start on a Monday. +DateTime+ objects have their time set to 0:00.
+ def monday
+ beginning_of_week
+ end
+
+ # Returns a new +Date+/+DateTime+ representing the end of this week. Week is
+ # assumed to start on +start_day+, default is +:monday+. +DateTime+ objects
+ # have their time set to 23:59:59.
+ def end_of_week(start_day = :monday)
+ days_to_end = 6 - days_to_week_start(start_day)
+ result = self + days_to_end.days
self.acts_like?(:time) ? result.end_of_day : result
end
- alias :sunday :end_of_week
alias :at_end_of_week :end_of_week
- # Returns a new Date/DateTime representing the start of the given day in the previous week (default is Monday).
+ # Returns a new +Date+/+DateTime+ representing the end of this week. Week is
+ # assumed to start on a Monday. +DateTime+ objects have their time set to 23:59:59.
+ def sunday
+ end_of_week
+ end
+
+ # Returns a new +Date+/+DateTime+ representing the given +day+ in the previous
+ # week. Default is +:monday+. +DateTime+ objects have their time set to 0:00.
def prev_week(day = :monday)
result = (self - 7).beginning_of_week + DAYS_INTO_WEEK[day]
self.acts_like?(:time) ? result.change(:hour => 0) : result
end
- # Returns a new Date/DateTime representing the start of the given day in next week (default is Monday).
+ # Returns a new Date/DateTime representing the start of the given day in next week (default is :monday).
def next_week(day = :monday)
result = (self + 7).beginning_of_week + DAYS_INTO_WEEK[day]
self.acts_like?(:time) ? result.change(:hour => 0) : result
diff --git a/activesupport/lib/active_support/core_ext/enumerable.rb b/activesupport/lib/active_support/core_ext/enumerable.rb
index ddb4f3012f..9343bb7106 100644
--- a/activesupport/lib/active_support/core_ext/enumerable.rb
+++ b/activesupport/lib/active_support/core_ext/enumerable.rb
@@ -97,7 +97,7 @@ module Enumerable
end
# Returns true if the enumerable has more than 1 element. Functionally equivalent to enum.to_a.size > 1.
- # Can be called with a block too, much like any?, so people.many? { |p| p.age > 26 } returns true if more than 1 person is over 26.
+ # Can be called with a block too, much like any?, so <tt>people.many? { |p| p.age > 26 }</tt> returns true if more than one person is over 26.
def many?
cnt = 0
if block_given?
@@ -110,7 +110,7 @@ module Enumerable
end
end
- # The negative of the Enumerable#include?. Returns true if the collection does not include the object.
+ # The negative of the <tt>Enumerable#include?</tt>. Returns true if the collection does not include the object.
def exclude?(object)
!include?(object)
end
diff --git a/activesupport/lib/active_support/core_ext/hash/indifferent_access.rb b/activesupport/lib/active_support/core_ext/hash/indifferent_access.rb
index 0b368fe7b7..f4cb445444 100644
--- a/activesupport/lib/active_support/core_ext/hash/indifferent_access.rb
+++ b/activesupport/lib/active_support/core_ext/hash/indifferent_access.rb
@@ -2,7 +2,7 @@ require 'active_support/hash_with_indifferent_access'
class Hash
- # Returns an +ActiveSupport::HashWithIndifferentAccess+ out of its receiver:
+ # Returns an <tt>ActiveSupport::HashWithIndifferentAccess</tt> out of its receiver:
#
# {:a => 1}.with_indifferent_access["a"] # => 1
#
diff --git a/activesupport/lib/active_support/core_ext/hash/slice.rb b/activesupport/lib/active_support/core_ext/hash/slice.rb
index d7fb2da0fb..0484d8e5d8 100644
--- a/activesupport/lib/active_support/core_ext/hash/slice.rb
+++ b/activesupport/lib/active_support/core_ext/hash/slice.rb
@@ -30,6 +30,8 @@ class Hash
omit
end
+ # Removes and returns the key/value pairs matching the given keys.
+ # {:a => 1, :b => 2, :c => 3, :d => 4}.extract!(:a, :b) # => {:a => 1, :b => 2}
def extract!(*keys)
result = {}
keys.each {|key| result[key] = delete(key) }
diff --git a/activesupport/lib/active_support/core_ext/io.rb b/activesupport/lib/active_support/core_ext/io.rb
new file mode 100644
index 0000000000..75f1055191
--- /dev/null
+++ b/activesupport/lib/active_support/core_ext/io.rb
@@ -0,0 +1,15 @@
+if RUBY_VERSION < '1.9.2'
+
+# :stopdoc:
+class IO
+ def self.binread(name, length = nil, offset = nil)
+ return File.read name unless length || offset
+ File.open(name, 'rb') { |f|
+ f.seek offset if offset
+ f.read length
+ }
+ end
+end
+# :startdoc:
+
+end
diff --git a/activesupport/lib/active_support/core_ext/module.rb b/activesupport/lib/active_support/core_ext/module.rb
index 9fed346b7c..f399fce410 100644
--- a/activesupport/lib/active_support/core_ext/module.rb
+++ b/activesupport/lib/active_support/core_ext/module.rb
@@ -8,4 +8,5 @@ require 'active_support/core_ext/module/delegation'
require 'active_support/core_ext/module/synchronization'
require 'active_support/core_ext/module/deprecation'
require 'active_support/core_ext/module/remove_method'
-require 'active_support/core_ext/module/method_names' \ No newline at end of file
+require 'active_support/core_ext/module/method_names'
+require 'active_support/core_ext/module/qualified_const' \ No newline at end of file
diff --git a/activesupport/lib/active_support/core_ext/module/delegation.rb b/activesupport/lib/active_support/core_ext/module/delegation.rb
index 41f9a76b13..7de824a77f 100644
--- a/activesupport/lib/active_support/core_ext/module/delegation.rb
+++ b/activesupport/lib/active_support/core_ext/module/delegation.rb
@@ -1,5 +1,3 @@
-require "active_support/core_ext/module/remove_method"
-
class Module
# Provides a delegate class method to easily expose contained objects' methods
# as your own. Pass one or more methods (specified as symbols or strings)
@@ -108,35 +106,48 @@ class Module
unless options.is_a?(Hash) && to = options[:to]
raise ArgumentError, "Delegation needs a target. Supply an options hash with a :to key as the last argument (e.g. delegate :hello, :to => :greeter)."
end
+ prefix, to, allow_nil = options[:prefix], options[:to], options[:allow_nil]
- if options[:prefix] == true && options[:to].to_s =~ /^[^a-z_]/
+ if prefix == true && to.to_s =~ /^[^a-z_]/
raise ArgumentError, "Can only automatically set the delegation prefix when delegating to a method."
end
- prefix = options[:prefix] ? "#{options[:prefix] == true ? to : options[:prefix]}_" : ''
+ method_prefix =
+ if prefix
+ "#{prefix == true ? to : prefix}_"
+ else
+ ''
+ end
file, line = caller.first.split(':', 2)
line = line.to_i
methods.each do |method|
- on_nil =
- if options[:allow_nil]
- 'return'
- else
- %(raise "#{self}##{prefix}#{method} delegated to #{to}.#{method}, but #{to} is nil: \#{self.inspect}")
- end
+ method = method.to_s
+
+ if allow_nil
+ module_eval(<<-EOS, file, line - 2)
+ def #{method_prefix}#{method}(*args, &block) # def customer_name(*args, &block)
+ if #{to} || #{to}.respond_to?(:#{method}) # if client || client.respond_to?(:name)
+ #{to}.__send__(:#{method}, *args, &block) # client.__send__(:name, *args, &block)
+ end # end
+ end # end
+ EOS
+ else
+ exception = %(raise "#{self}##{method_prefix}#{method} delegated to #{to}.#{method}, but #{to} is nil: \#{self.inspect}")
- module_eval(<<-EOS, file, line - 5)
- def #{prefix}#{method}(*args, &block) # def customer_name(*args, &block)
- #{to}.__send__(#{method.inspect}, *args, &block) # client.__send__(:name, *args, &block)
- rescue NoMethodError # rescue NoMethodError
- if #{to}.nil? # if client.nil?
- #{on_nil} # return # depends on :allow_nil
- else # else
- raise # raise
- end # end
- end # end
- EOS
+ module_eval(<<-EOS, file, line - 1)
+ def #{method_prefix}#{method}(*args, &block) # def customer_name(*args, &block)
+ #{to}.__send__(:#{method}, *args, &block) # client.__send__(:name, *args, &block)
+ rescue NoMethodError # rescue NoMethodError
+ if #{to}.nil? # if client.nil?
+ #{exception} # # add helpful message to the exception
+ else # else
+ raise # raise
+ end # end
+ end # end
+ EOS
+ end
end
end
end
diff --git a/activesupport/lib/active_support/core_ext/module/qualified_const.rb b/activesupport/lib/active_support/core_ext/module/qualified_const.rb
new file mode 100644
index 0000000000..d1a0ee2f83
--- /dev/null
+++ b/activesupport/lib/active_support/core_ext/module/qualified_const.rb
@@ -0,0 +1,64 @@
+require 'active_support/core_ext/string/inflections'
+
+#--
+# Allows code reuse in the methods below without polluting Module.
+#++
+module QualifiedConstUtils
+ def self.raise_if_absolute(path)
+ raise NameError, "wrong constant name #$&" if path =~ /\A::[^:]+/
+ end
+
+ def self.names(path)
+ path.split('::')
+ end
+end
+
+##
+# Extends the API for constants to be able to deal with qualified names. Arguments
+# are assumed to be relative to the receiver.
+#
+#--
+# Qualified names are required to be relative because we are extending existing
+# methods that expect constant names, ie, relative paths of length 1. For example,
+# Object.const_get("::String") raises NameError and so does qualified_const_get.
+#++
+class Module
+ if method(:const_defined?).arity == 1
+ def qualified_const_defined?(path)
+ QualifiedConstUtils.raise_if_absolute(path)
+
+ QualifiedConstUtils.names(path).inject(self) do |mod, name|
+ return unless mod.const_defined?(name)
+ mod.const_get(name)
+ end
+ return true
+ end
+ else
+ def qualified_const_defined?(path, search_parents=true)
+ QualifiedConstUtils.raise_if_absolute(path)
+
+ QualifiedConstUtils.names(path).inject(self) do |mod, name|
+ return unless mod.const_defined?(name, search_parents)
+ mod.const_get(name)
+ end
+ return true
+ end
+ end
+
+ def qualified_const_get(path)
+ QualifiedConstUtils.raise_if_absolute(path)
+
+ QualifiedConstUtils.names(path).inject(self) do |mod, name|
+ mod.const_get(name)
+ end
+ end
+
+ def qualified_const_set(path, value)
+ QualifiedConstUtils.raise_if_absolute(path)
+
+ const_name = path.demodulize
+ mod_name = path.deconstantize
+ mod = mod_name.empty? ? self : qualified_const_get(mod_name)
+ mod.const_set(const_name, value)
+ end
+end
diff --git a/activesupport/lib/active_support/core_ext/module/reachable.rb b/activesupport/lib/active_support/core_ext/module/reachable.rb
index 443d2c3d53..5d3d0e9851 100644
--- a/activesupport/lib/active_support/core_ext/module/reachable.rb
+++ b/activesupport/lib/active_support/core_ext/module/reachable.rb
@@ -3,8 +3,6 @@ require 'active_support/core_ext/string/inflections'
class Module
def reachable? #:nodoc:
- !anonymous? && name.constantize.equal?(self)
- rescue NameError
- false
+ !anonymous? && name.safe_constantize.equal?(self)
end
end
diff --git a/activesupport/lib/active_support/core_ext/module/remove_method.rb b/activesupport/lib/active_support/core_ext/module/remove_method.rb
index 07d7c9b018..b76bc16ee1 100644
--- a/activesupport/lib/active_support/core_ext/module/remove_method.rb
+++ b/activesupport/lib/active_support/core_ext/module/remove_method.rb
@@ -1,11 +1,16 @@
class Module
def remove_possible_method(method)
- remove_method(method)
+ if method_defined?(method) || private_method_defined?(method)
+ remove_method(method)
+ end
rescue NameError
+ # If the requested method is defined on a superclass or included module,
+ # method_defined? returns true but remove_method throws a NameError.
+ # Ignore this.
end
def redefine_method(method, &block)
remove_possible_method(method)
define_method(method, &block)
end
-end \ No newline at end of file
+end
diff --git a/activesupport/lib/active_support/core_ext/module/synchronization.rb b/activesupport/lib/active_support/core_ext/module/synchronization.rb
index ed16c2f71b..061621c0ef 100644
--- a/activesupport/lib/active_support/core_ext/module/synchronization.rb
+++ b/activesupport/lib/active_support/core_ext/module/synchronization.rb
@@ -1,6 +1,7 @@
require 'thread'
require 'active_support/core_ext/module/aliasing'
require 'active_support/core_ext/array/extract_options'
+require 'active_support/core_ext/module/deprecation'
class Module
# Synchronize access around a method, delegating synchronization to a
@@ -40,4 +41,5 @@ class Module
alias_method_chain method, :synchronization
end
end
+ deprecate :synchronize
end
diff --git a/activesupport/lib/active_support/core_ext/object/inclusion.rb b/activesupport/lib/active_support/core_ext/object/inclusion.rb
index b5671f66d0..f611cdd606 100644
--- a/activesupport/lib/active_support/core_ext/object/inclusion.rb
+++ b/activesupport/lib/active_support/core_ext/object/inclusion.rb
@@ -1,15 +1,25 @@
class Object
- # Returns true if this object is included in the argument. Argument must be
- # any object which responds to +#include?+. Usage:
+ # Returns true if this object is included in the argument(s). Argument must be
+ # any object which responds to +#include?+ or optionally, multiple arguments can be passed in. Usage:
#
# characters = ["Konata", "Kagami", "Tsukasa"]
# "Konata".in?(characters) # => true
+ #
+ # character = "Konata"
+ # character.in?("Konata", "Kagami", "Tsukasa") # => true
#
- # This will throw an ArgumentError if the argument doesn't respond
+ # This will throw an ArgumentError if a single argument is passed in and it doesn't respond
# to +#include?+.
- def in?(another_object)
- another_object.include?(self)
- rescue NoMethodError
- raise ArgumentError.new("The parameter passed to #in? must respond to #include?")
+ def in?(*args)
+ if args.length > 1
+ args.include? self
+ else
+ another_object = args.first
+ if another_object.respond_to? :include?
+ another_object.include? self
+ else
+ raise ArgumentError.new("The single parameter passed to #in? must respond to #include?")
+ end
+ end
end
end
diff --git a/activesupport/lib/active_support/core_ext/object/to_json.rb b/activesupport/lib/active_support/core_ext/object/to_json.rb
index 14ef27340e..e7dc60a612 100644
--- a/activesupport/lib/active_support/core_ext/object/to_json.rb
+++ b/activesupport/lib/active_support/core_ext/object/to_json.rb
@@ -10,10 +10,10 @@ end
# several cases (for instance, the JSON implementation for Hash does not work) with inheritance
# and consequently classes as ActiveSupport::OrderedHash cannot be serialized to json.
[Object, Array, FalseClass, Float, Hash, Integer, NilClass, String, TrueClass].each do |klass|
- klass.class_eval <<-RUBY, __FILE__, __LINE__
+ klass.class_eval do
# Dumps object in JSON (JavaScript Object Notation). See www.json.org for more info.
def to_json(options = nil)
ActiveSupport::JSON.encode(self, options)
end
- RUBY
+ end
end
diff --git a/activesupport/lib/active_support/core_ext/object/to_query.rb b/activesupport/lib/active_support/core_ext/object/to_query.rb
index 3f1540f685..5d5fcf00e0 100644
--- a/activesupport/lib/active_support/core_ext/object/to_query.rb
+++ b/activesupport/lib/active_support/core_ext/object/to_query.rb
@@ -7,7 +7,7 @@ class Object
# Note: This method is defined as a default implementation for all Objects for Hash#to_query to work.
def to_query(key)
require 'cgi' unless defined?(CGI) && defined?(CGI::escape)
- "#{CGI.escape(key.to_s)}=#{CGI.escape(to_param.to_s)}"
+ "#{CGI.escape(key.to_param)}=#{CGI.escape(to_param.to_s)}"
end
end
diff --git a/activesupport/lib/active_support/core_ext/object/try.rb b/activesupport/lib/active_support/core_ext/object/try.rb
index 4797c93e63..e77a9da0ec 100644
--- a/activesupport/lib/active_support/core_ext/object/try.rb
+++ b/activesupport/lib/active_support/core_ext/object/try.rb
@@ -28,8 +28,6 @@ class Object
def try(*a, &b)
if a.empty? && block_given?
yield self
- elsif !a.empty? && !respond_to?(a.first)
- nil
else
__send__(*a, &b)
end
diff --git a/activesupport/lib/active_support/core_ext/range/conversions.rb b/activesupport/lib/active_support/core_ext/range/conversions.rb
index 544e63132d..43134b4314 100644
--- a/activesupport/lib/active_support/core_ext/range/conversions.rb
+++ b/activesupport/lib/active_support/core_ext/range/conversions.rb
@@ -7,7 +7,7 @@ class Range
#
# ==== Example
#
- # [1..100].to_formatted_s # => "1..100"
+ # (1..100).to_formatted_s # => "1..100"
def to_formatted_s(format = :default)
if formatter = RANGE_FORMATS[format]
formatter.call(first, last)
diff --git a/activesupport/lib/active_support/core_ext/string/inflections.rb b/activesupport/lib/active_support/core_ext/string/inflections.rb
index 002688d6c0..1e57b586d9 100644
--- a/activesupport/lib/active_support/core_ext/string/inflections.rb
+++ b/activesupport/lib/active_support/core_ext/string/inflections.rb
@@ -9,14 +9,25 @@ require 'active_support/inflector/transliterate'
class String
# Returns the plural form of the word in the string.
#
+ # If the optional parameter +count+ is specified,
+ # the singular form will be returned if <tt>count == 1</tt>.
+ # For any other value of +count+ the plural will be returned.
+ #
+ # ==== Examples
# "post".pluralize # => "posts"
# "octopus".pluralize # => "octopi"
# "sheep".pluralize # => "sheep"
# "words".pluralize # => "words"
# "the blue mailman".pluralize # => "the blue mailmen"
# "CamelOctopus".pluralize # => "CamelOctopi"
- def pluralize
- ActiveSupport::Inflector.pluralize(self)
+ # "apple".pluralize(1) # => "apple"
+ # "apple".pluralize(2) # => "apples"
+ def pluralize(count = nil)
+ if count == 1
+ self
+ else
+ ActiveSupport::Inflector.pluralize(self)
+ end
end
# The reverse of +pluralize+, returns the singular form of a word in a string.
@@ -33,15 +44,28 @@ class String
# +constantize+ tries to find a declared constant with the name specified
# in the string. It raises a NameError when the name is not in CamelCase
- # or is not initialized.
+ # or is not initialized. See ActiveSupport::Inflector.constantize
#
# Examples
- # "Module".constantize # => Module
- # "Class".constantize # => Class
+ # "Module".constantize # => Module
+ # "Class".constantize # => Class
+ # "blargle".constantize # => NameError: wrong constant name blargle
def constantize
ActiveSupport::Inflector.constantize(self)
end
+ # +safe_constantize+ tries to find a declared constant with the name specified
+ # in the string. It returns nil when the name is not in CamelCase
+ # or is not initialized. See ActiveSupport::Inflector.safe_constantize
+ #
+ # Examples
+ # "Module".safe_constantize # => Module
+ # "Class".safe_constantize # => Class
+ # "blargle".safe_constantize # => nil
+ def safe_constantize
+ ActiveSupport::Inflector.safe_constantize(self)
+ end
+
# By default, +camelize+ converts strings to UpperCamelCase. If the argument to camelize
# is set to <tt>:lower</tt> then camelize produces lowerCamelCase.
#
@@ -93,10 +117,25 @@ class String
#
# "ActiveRecord::CoreExtensions::String::Inflections".demodulize # => "Inflections"
# "Inflections".demodulize # => "Inflections"
+ #
+ # See also +deconstantize+.
def demodulize
ActiveSupport::Inflector.demodulize(self)
end
+ # Removes the rightmost segment from the constant expression in the string.
+ #
+ # "Net::HTTP".deconstantize # => "Net"
+ # "::Net::HTTP".deconstantize # => "::Net"
+ # "String".deconstantize # => ""
+ # "::String".deconstantize # => ""
+ # "".deconstantize # => ""
+ #
+ # See also +demodulize+.
+ def deconstantize
+ ActiveSupport::Inflector.deconstantize(self)
+ end
+
# Replaces special characters in a string so that it may be used as part of a 'pretty' URL.
#
# ==== Examples
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 3bf4edbdef..5d7f74bb65 100644
--- a/activesupport/lib/active_support/core_ext/string/output_safety.rb
+++ b/activesupport/lib/active_support/core_ext/string/output_safety.rb
@@ -20,7 +20,7 @@ class ERB
if s.html_safe?
s
else
- s.gsub(/[&"><]/) { |special| HTML_ESCAPE[special] }.html_safe
+ s.gsub(/&/, "&amp;").gsub(/\"/, "&quot;").gsub(/>/, "&gt;").gsub(/</, "&lt;").html_safe
end
end
@@ -75,7 +75,7 @@ end
module ActiveSupport #:nodoc:
class SafeBuffer < String
- UNSAFE_STRING_METHODS = ["capitalize", "chomp", "chop", "delete", "downcase", "gsub", "lstrip", "next", "reverse", "rstrip", "slice", "squeeze", "strip", "sub", "succ", "swapcase", "tr", "tr_s", "upcase"].freeze
+ UNSAFE_STRING_METHODS = ["capitalize", "chomp", "chop", "delete", "downcase", "gsub", "lstrip", "next", "reverse", "rstrip", "slice", "squeeze", "strip", "sub", "succ", "swapcase", "tr", "tr_s", "upcase", "prepend"].freeze
alias_method :original_concat, :concat
private :original_concat
@@ -86,6 +86,12 @@ module ActiveSupport #:nodoc:
end
end
+ def[](*args)
+ new_safe_buffer = super
+ new_safe_buffer.instance_eval { @dirty = false }
+ new_safe_buffer
+ end
+
def safe_concat(value)
raise SafeConcatError if dirty?
original_concat(value)
@@ -136,16 +142,18 @@ module ActiveSupport #:nodoc:
end
UNSAFE_STRING_METHODS.each do |unsafe_method|
- class_eval <<-EOT, __FILE__, __LINE__
- def #{unsafe_method}(*args, &block) # def gsub(*args, &block)
- to_str.#{unsafe_method}(*args, &block) # to_str.gsub(*args, &block)
- end # end
-
- def #{unsafe_method}!(*args) # def gsub!(*args)
- @dirty = true # @dirty = true
- super # super
- end # end
- EOT
+ if 'String'.respond_to?(unsafe_method)
+ class_eval <<-EOT, __FILE__, __LINE__ + 1
+ def #{unsafe_method}(*args, &block) # def capitalize(*args, &block)
+ to_str.#{unsafe_method}(*args, &block) # to_str.capitalize(*args, &block)
+ end # end
+
+ def #{unsafe_method}!(*args) # def capitalize!(*args)
+ @dirty = true # @dirty = true
+ super # super
+ end # end
+ EOT
+ end
end
protected
diff --git a/activesupport/lib/active_support/core_ext/time/calculations.rb b/activesupport/lib/active_support/core_ext/time/calculations.rb
index c5fbbcf890..f3235d11bb 100644
--- a/activesupport/lib/active_support/core_ext/time/calculations.rb
+++ b/activesupport/lib/active_support/core_ext/time/calculations.rb
@@ -1,5 +1,6 @@
require 'active_support/duration'
require 'active_support/core_ext/time/zones'
+require 'active_support/core_ext/time/conversions'
class Time
COMMON_YEAR_DAYS_IN_MONTH = [nil, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31]
@@ -8,7 +9,7 @@ class Time
class << self
# Overriding case equality method so that it returns true for ActiveSupport::TimeWithZone instances
def ===(other)
- other.is_a?(::Time)
+ super || (self == Time && other.is_a?(ActiveSupport::TimeWithZone))
end
# Return the number of days in the given month.
@@ -160,27 +161,46 @@ class Time
months_since(1)
end
- # Returns a new Time representing the "start" of this week (Monday, 0:00)
- def beginning_of_week
- days_to_monday = wday!=0 ? wday-1 : 6
- (self - days_to_monday.days).midnight
+ # Returns number of days to start of this week, week starts on start_day (default is :monday).
+ def days_to_week_start(start_day = :monday)
+ start_day_number = DAYS_INTO_WEEK[start_day]
+ current_day_number = wday != 0 ? wday - 1 : 6
+ days_span = current_day_number - start_day_number
+ days_span >= 0 ? days_span : 7 + days_span
+ end
+
+ # Returns a new Time representing the "start" of this week, week starts on start_day (default is :monday, i.e. Monday, 0:00).
+ def beginning_of_week(start_day = :monday)
+ days_to_start = days_to_week_start(start_day)
+ (self - days_to_start.days).midnight
end
- alias :monday :beginning_of_week
alias :at_beginning_of_week :beginning_of_week
- # Returns a new Time representing the end of this week, (end of Sunday)
- def end_of_week
- days_to_sunday = wday!=0 ? 7-wday : 0
- (self + days_to_sunday.days).end_of_day
+ # Returns a new +Date+/+DateTime+ representing the start of this week. Week is
+ # assumed to start on a Monday. +DateTime+ objects have their time set to 0:00.
+ def monday
+ beginning_of_week
+ end
+
+ # Returns a new Time representing the end of this week, week starts on start_day (default is :monday, i.e. end of Sunday).
+ def end_of_week(start_day = :monday)
+ days_to_end = 6 - days_to_week_start(start_day)
+ (self + days_to_end.days).end_of_day
end
alias :at_end_of_week :end_of_week
- # Returns a new Time representing the start of the given day in the previous week (default is Monday).
+ # Returns a new +Date+/+DateTime+ representing the end of this week. Week is
+ # assumed to start on a Monday. +DateTime+ objects have their time set to 23:59:59.
+ def sunday
+ end_of_week
+ end
+
+ # Returns a new Time representing the start of the given day in the previous week (default is :monday).
def prev_week(day = :monday)
ago(1.week).beginning_of_week.since(DAYS_INTO_WEEK[day].day).change(:hour => 0)
end
- # Returns a new Time representing the start of the given day in next week (default is Monday).
+ # Returns a new Time representing the start of the given day in next week (default is :monday).
def next_week(day = :monday)
since(1.week).beginning_of_week.since(DAYS_INTO_WEEK[day].day).change(:hour => 0)
end
@@ -311,4 +331,14 @@ class Time
end
alias_method :compare_without_coercion, :<=>
alias_method :<=>, :compare_with_coercion
+
+ # Layers additional behavior on Time#eql? so that ActiveSupport::TimeWithZone instances
+ # can be eql? to an equivalent Time
+ def eql_with_coercion(other)
+ # if other is an ActiveSupport::TimeWithZone, coerce a Time instance from it so we can do eql? comparison
+ other = other.comparable_time if other.respond_to?(:comparable_time)
+ eql_without_coercion(other)
+ end
+ alias_method :eql_without_coercion, :eql?
+ alias_method :eql?, :eql_with_coercion
end
diff --git a/activesupport/lib/active_support/core_ext/time/conversions.rb b/activesupport/lib/active_support/core_ext/time/conversions.rb
index d9d5e02778..49ac18d245 100644
--- a/activesupport/lib/active_support/core_ext/time/conversions.rb
+++ b/activesupport/lib/active_support/core_ext/time/conversions.rb
@@ -55,9 +55,31 @@ 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