aboutsummaryrefslogtreecommitdiffstats
path: root/activesupport/lib
diff options
context:
space:
mode:
Diffstat (limited to 'activesupport/lib')
-rw-r--r--activesupport/lib/active_support.rb3
-rw-r--r--activesupport/lib/active_support/cache/file_store.rb7
-rw-r--r--activesupport/lib/active_support/cache/mem_cache_store.rb53
-rw-r--r--activesupport/lib/active_support/callbacks.rb5
-rw-r--r--activesupport/lib/active_support/concurrency/latch.rb27
-rw-r--r--activesupport/lib/active_support/core_ext/date.rb5
-rw-r--r--activesupport/lib/active_support/core_ext/date/calculations.rb184
-rw-r--r--activesupport/lib/active_support/core_ext/date_and_time/calculations.rb213
-rw-r--r--activesupport/lib/active_support/core_ext/date_time.rb4
-rw-r--r--activesupport/lib/active_support/core_ext/date_time/calculations.rb9
-rw-r--r--activesupport/lib/active_support/core_ext/file/atomic.rb32
-rw-r--r--activesupport/lib/active_support/core_ext/object/to_param.rb7
-rw-r--r--activesupport/lib/active_support/core_ext/object/try.rb17
-rw-r--r--activesupport/lib/active_support/core_ext/string.rb1
-rw-r--r--activesupport/lib/active_support/core_ext/string/indent.rb43
-rw-r--r--activesupport/lib/active_support/core_ext/string/inflections.rb22
-rw-r--r--activesupport/lib/active_support/core_ext/string/output_safety.rb13
-rw-r--r--activesupport/lib/active_support/core_ext/time.rb5
-rw-r--r--activesupport/lib/active_support/core_ext/time/calculations.rb211
-rw-r--r--activesupport/lib/active_support/core_ext/time/zones.rb11
-rw-r--r--activesupport/lib/active_support/dependencies.rb53
-rw-r--r--activesupport/lib/active_support/dependencies/autoload.rb60
-rw-r--r--activesupport/lib/active_support/duration.rb2
-rw-r--r--activesupport/lib/active_support/hash_with_indifferent_access.rb121
-rw-r--r--activesupport/lib/active_support/inflections.rb2
-rw-r--r--activesupport/lib/active_support/inflector/inflections.rb23
-rw-r--r--activesupport/lib/active_support/inflector/methods.rb22
-rw-r--r--activesupport/lib/active_support/json/encoding.rb64
-rw-r--r--activesupport/lib/active_support/json/variable.rb17
-rw-r--r--activesupport/lib/active_support/locale/en.yml10
-rw-r--r--activesupport/lib/active_support/notifications/fanout.rb6
-rw-r--r--activesupport/lib/active_support/number_helper.rb161
-rw-r--r--activesupport/lib/active_support/rails.rb27
-rw-r--r--activesupport/lib/active_support/railtie.rb2
-rw-r--r--activesupport/lib/active_support/string_inquirer.rb18
-rw-r--r--activesupport/lib/active_support/test_case.rb29
-rw-r--r--activesupport/lib/active_support/testing/declarative.rb40
-rw-r--r--activesupport/lib/active_support/time_with_zone.rb3
38 files changed, 893 insertions, 639 deletions
diff --git a/activesupport/lib/active_support.rb b/activesupport/lib/active_support.rb
index 56d6676961..41d77ab6c1 100644
--- a/activesupport/lib/active_support.rb
+++ b/activesupport/lib/active_support.rb
@@ -37,7 +37,6 @@ module ActiveSupport
autoload :LogSubscriber
autoload :Notifications
- # TODO: Narrow this list down
eager_autoload do
autoload :BacktraceCleaner
autoload :BasicObject
@@ -55,12 +54,12 @@ module ActiveSupport
autoload :OptionMerger
autoload :OrderedHash
autoload :OrderedOptions
- autoload :Rescuable
autoload :StringInquirer
autoload :TaggedLogging
autoload :XmlMini
end
+ autoload :Rescuable
autoload :SafeBuffer, "active_support/core_ext/string/output_safety"
autoload :TestCase
end
diff --git a/activesupport/lib/active_support/cache/file_store.rb b/activesupport/lib/active_support/cache/file_store.rb
index 5be63af342..2c1ad60d44 100644
--- a/activesupport/lib/active_support/cache/file_store.rb
+++ b/activesupport/lib/active_support/cache/file_store.rb
@@ -1,6 +1,5 @@
require 'active_support/core_ext/file/atomic'
require 'active_support/core_ext/string/conversions'
-require 'active_support/core_ext/object/inclusion'
require 'uri/common'
module ActiveSupport
@@ -23,7 +22,7 @@ module ActiveSupport
end
def clear(options = nil)
- root_dirs = Dir.entries(cache_path).reject{|f| f.in?(EXCLUDED_DIRS + [".gitkeep"])}
+ root_dirs = Dir.entries(cache_path).reject {|f| (EXCLUDED_DIRS + [".gitkeep"]).include?(f)}
FileUtils.rm_r(root_dirs.collect{|f| File.join(cache_path, f)})
end
@@ -151,7 +150,7 @@ module ActiveSupport
# Delete empty directories in the cache.
def delete_empty_directories(dir)
return if dir == cache_path
- if Dir.entries(dir).reject{|f| f.in?(EXCLUDED_DIRS)}.empty?
+ if Dir.entries(dir).reject {|f| EXCLUDED_DIRS.include?(f)}.empty?
File.delete(dir) rescue nil
delete_empty_directories(File.dirname(dir))
end
@@ -165,7 +164,7 @@ module ActiveSupport
def search_dir(dir, &callback)
return if !File.exist?(dir)
Dir.foreach(dir) do |d|
- next if d.in?(EXCLUDED_DIRS)
+ next if EXCLUDED_DIRS.include?(d)
name = File.join(dir, d)
if File.directory?(name)
search_dir(name, &callback)
diff --git a/activesupport/lib/active_support/cache/mem_cache_store.rb b/activesupport/lib/active_support/cache/mem_cache_store.rb
index 2e1ccb72d8..5aa78cc9f3 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'
+ require 'dalli'
rescue LoadError => e
- $stderr.puts "You don't have memcache-client installed in your application. Please add it to your Gemfile and run bundle install"
+ $stderr.puts "You don't have dalli installed in your application. Please add it to your Gemfile and run bundle install"
raise e
end
@@ -22,21 +22,13 @@ module ActiveSupport
# MemCacheStore implements the Strategy::LocalCache strategy which implements
# an in-memory cache inside of a block.
class MemCacheStore < Store
- module Response # :nodoc:
- STORED = "STORED\r\n"
- NOT_STORED = "NOT_STORED\r\n"
- EXISTS = "EXISTS\r\n"
- NOT_FOUND = "NOT_FOUND\r\n"
- DELETED = "DELETED\r\n"
- end
-
ESCAPE_KEY_CHARS = /[\x00-\x20%\x7F-\xFF]/n
def self.build_mem_cache(*addresses)
addresses = addresses.flatten
options = addresses.extract_options!
addresses = ["localhost:11211"] if addresses.empty?
- MemCache.new(addresses, options)
+ Dalli::Client.new(addresses, options)
end
# Creates a new MemCacheStore object, with the given memcached server
@@ -90,11 +82,11 @@ module ActiveSupport
# to zero.
def increment(name, amount = 1, options = nil) # :nodoc:
options = merged_options(options)
- response = instrument(:increment, name, :amount => amount) do
+ instrument(:increment, name, :amount => amount) do
@data.incr(escape_key(namespaced_key(name, options)), amount)
end
- response == Response::NOT_FOUND ? nil : response.to_i
- rescue MemCache::MemCacheError
+ rescue Dalli::DalliError
+ logger.error("DalliError (#{e}): #{e.message}") if logger
nil
end
@@ -104,11 +96,11 @@ module ActiveSupport
# to zero.
def decrement(name, amount = 1, options = nil) # :nodoc:
options = merged_options(options)
- response = instrument(:decrement, name, :amount => amount) do
+ instrument(:decrement, name, :amount => amount) do
@data.decr(escape_key(namespaced_key(name, options)), amount)
end
- response == Response::NOT_FOUND ? nil : response.to_i
- rescue MemCache::MemCacheError
+ rescue Dalli::DalliError
+ logger.error("DalliError (#{e}): #{e.message}") if logger
nil
end
@@ -116,6 +108,9 @@ module ActiveSupport
# be used with care when shared cache is being used.
def clear(options = nil)
@data.flush_all
+ rescue Dalli::DalliError => e
+ logger.error("DalliError (#{e}): #{e.message}") if logger
+ nil
end
# Get the statistics from the memcached servers.
@@ -126,9 +121,9 @@ module ActiveSupport
protected
# Read an entry from the cache.
def read_entry(key, options) # :nodoc:
- deserialize_entry(@data.get(escape_key(key), true))
- rescue MemCache::MemCacheError => e
- logger.error("MemCacheError (#{e}): #{e.message}") if logger
+ deserialize_entry(@data.get(escape_key(key), options))
+ rescue Dalli::DalliError => e
+ logger.error("DalliError (#{e}): #{e.message}") if logger
nil
end
@@ -137,23 +132,17 @@ module ActiveSupport
method = options && options[:unless_exist] ? :add : :set
value = options[:raw] ? entry.value.to_s : entry
expires_in = options[:expires_in].to_i
- if expires_in > 0 && !options[:raw]
- # Set the memcache expire a few minutes in the future to support race condition ttls on read
- expires_in += 5.minutes
- end
- response = @data.send(method, escape_key(key), value, expires_in, options[:raw])
- response == Response::STORED
- rescue MemCache::MemCacheError => e
- logger.error("MemCacheError (#{e}): #{e.message}") if logger
+ @data.send(method, escape_key(key), value, expires_in, options)
+ rescue Dalli::DalliError => e
+ logger.error("DalliError (#{e}): #{e.message}") if logger
false
end
# Delete an entry from the cache.
def delete_entry(key, options) # :nodoc:
- response = @data.delete(escape_key(key))
- response == Response::DELETED
- rescue MemCache::MemCacheError => e
- logger.error("MemCacheError (#{e}): #{e.message}") if logger
+ @data.delete(escape_key(key))
+ rescue Dalli::DalliError => e
+ logger.error("DalliError (#{e}): #{e.message}") if logger
false
end
diff --git a/activesupport/lib/active_support/callbacks.rb b/activesupport/lib/active_support/callbacks.rb
index 6cc875c69a..3f7d0e401a 100644
--- a/activesupport/lib/active_support/callbacks.rb
+++ b/activesupport/lib/active_support/callbacks.rb
@@ -3,7 +3,6 @@ require 'active_support/descendants_tracker'
require 'active_support/core_ext/class/attribute'
require 'active_support/core_ext/kernel/reporting'
require 'active_support/core_ext/kernel/singleton_class'
-require 'active_support/core_ext/object/inclusion'
module ActiveSupport
# \Callbacks are code hooks that are run at key points in an object's lifecycle.
@@ -78,7 +77,7 @@ module ActiveSupport
private
# A hook invoked everytime a before callback is halted.
- # This can be overriden in AS::Callback implementors in order
+ # This can be overridden in AS::Callback implementors in order
# to provide better debugging/logging.
def halted_callback_hook(filter)
end
@@ -353,7 +352,7 @@ module ActiveSupport
# CallbackChain.
#
def __update_callbacks(name, filters = [], block = nil) #:nodoc:
- type = filters.first.in?([:before, :after, :around]) ? filters.shift : :before
+ type = [:before, :after, :around].include?(filters.first) ? filters.shift : :before
options = filters.last.is_a?(Hash) ? filters.pop : {}
filters.unshift(block) if block
diff --git a/activesupport/lib/active_support/concurrency/latch.rb b/activesupport/lib/active_support/concurrency/latch.rb
new file mode 100644
index 0000000000..1507de433e
--- /dev/null
+++ b/activesupport/lib/active_support/concurrency/latch.rb
@@ -0,0 +1,27 @@
+require 'thread'
+require 'monitor'
+
+module ActiveSupport
+ module Concurrency
+ class Latch
+ def initialize(count = 1)
+ @count = count
+ @lock = Monitor.new
+ @cv = @lock.new_cond
+ end
+
+ def release
+ @lock.synchronize do
+ @count -= 1 if @count > 0
+ @cv.broadcast if @count.zero?
+ end
+ end
+
+ def await
+ @lock.synchronize do
+ @cv.wait_while { @count > 0 }
+ end
+ end
+ end
+ end
+end
diff --git a/activesupport/lib/active_support/core_ext/date.rb b/activesupport/lib/active_support/core_ext/date.rb
new file mode 100644
index 0000000000..465fedda80
--- /dev/null
+++ b/activesupport/lib/active_support/core_ext/date.rb
@@ -0,0 +1,5 @@
+require 'active_support/core_ext/date/acts_like'
+require 'active_support/core_ext/date/calculations'
+require 'active_support/core_ext/date/conversions'
+require 'active_support/core_ext/date/zones'
+
diff --git a/activesupport/lib/active_support/core_ext/date/calculations.rb b/activesupport/lib/active_support/core_ext/date/calculations.rb
index 7fe4161fb4..86badf4d29 100644
--- a/activesupport/lib/active_support/core_ext/date/calculations.rb
+++ b/activesupport/lib/active_support/core_ext/date/calculations.rb
@@ -3,17 +3,10 @@ require 'active_support/duration'
require 'active_support/core_ext/object/acts_like'
require 'active_support/core_ext/date/zones'
require 'active_support/core_ext/time/zones'
+require 'active_support/core_ext/date_and_time/calculations'
class Date
- DAYS_INTO_WEEK = {
- :monday => 0,
- :tuesday => 1,
- :wednesday => 2,
- :thursday => 3,
- :friday => 4,
- :saturday => 5,
- :sunday => 6
- }
+ include DateAndTime::Calculations
class << self
# Returns a new Date representing the date 1 day ago (i.e. yesterday's date).
@@ -32,21 +25,6 @@ class Date
end
end
- # Returns true if the Date object's date lies in the past. Otherwise returns false.
- def past?
- self < ::Date.current
- end
-
- # Returns true if the Date object's date is today.
- def today?
- to_date == ::Date.current # we need the to_date because of DateTime
- end
-
- # Returns true if the Date object's date lies in the future.
- def future?
- self > ::Date.current
- end
-
# Converts Date to a Time (or DateTime if necessary) with the time portion set to the beginning of the day (0:00)
# and then subtracts the specified number of seconds.
def ago(seconds)
@@ -106,6 +84,7 @@ class Date
end
# Returns a new Date where one or more of the elements have been changed according to the +options+ parameter.
+ # The +options+ parameter is a hash with a combination of these keys: <tt>:year</tt>, <tt>:month</tt>, <tt>:day</tt>.
#
# Date.new(2007, 5, 12).change(:day => 1) # => Date.new(2007, 5, 1)
# Date.new(2007, 5, 12).change(:year => 2005, :month => 1) # => Date.new(2005, 1, 12)
@@ -116,161 +95,4 @@ class Date
options.fetch(:day, day)
)
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)
- end
-
- # Returns a new Date/DateTime representing the time a number of specified months in the future.
- def months_since(months)
- advance(:months => months)
- end
-
- # Returns a new Date/DateTime representing the time a number of specified years ago.
- def years_ago(years)
- advance(:years => -years)
- end
-
- # Returns a new Date/DateTime representing the time a number of specified years in the future.
- def years_since(years)
- advance(:years => years)
- end
-
- # 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 :at_beginning_of_week :beginning_of_week
-
- # Returns a new +Date+/+DateTime+ representing the start of this week. Week is
- # assumed to start on a Monday. +DateTime+ objects have their time set to 0:00.
- def monday
- beginning_of_week
- end
-
- # Returns a new +Date+/+DateTime+ representing the end of this week. Week is
- # assumed to start on +start_day+, default is +:monday+. +DateTime+ objects
- # have their time set to 23:59:59.
- def end_of_week(start_day = :monday)
- days_to_end = 6 - days_to_week_start(start_day)
- result = self + days_to_end.days
- acts_like?(:time) ? result.end_of_day : result
- end
- alias :at_end_of_week :end_of_week
-
- # 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]
- acts_like?(:time) ? result.change(:hour => 0) : result
- end
- alias :last_week :prev_week
-
- # Alias of prev_month
- alias :last_month :prev_month
-
- # Alias of prev_year
- alias :last_year :prev_year
-
- # Returns a new Date/DateTime representing the start of the given day in next week (default is :monday).
- def next_week(day = :monday)
- result = (self + 7).beginning_of_week + DAYS_INTO_WEEK[day]
- acts_like?(:time) ? result.change(:hour => 0) : result
- end
-
- # Short-hand for months_ago(3)
- def prev_quarter
- months_ago(3)
- end
- alias_method :last_quarter, :prev_quarter
-
- # Short-hand for months_since(3)
- def next_quarter
- months_since(3)
- end
-
- # Returns a new Date/DateTime representing the start of the month (1st of the month; DateTime objects will have time set to 0:00)
- def beginning_of_month
- acts_like?(:time) ? change(:day => 1, :hour => 0) : change(:day => 1)
- end
- alias :at_beginning_of_month :beginning_of_month
-
- # Returns a new Date/DateTime representing the end of the month (last day of the month; DateTime objects will have time set to 0:00)
- def end_of_month
- last_day = ::Time.days_in_month(month, year)
- if acts_like?(:time)
- change(:day => last_day, :hour => 23, :min => 59, :sec => 59)
- else
- change(:day => last_day)
- end
- end
- alias :at_end_of_month :end_of_month
-
- # Returns a new Date/DateTime representing the start of the quarter (1st of january, april, july, october; DateTime objects will have time set to 0:00)
- def beginning_of_quarter
- first_quarter_month = [10, 7, 4, 1].detect { |m| m <= month }
- beginning_of_month.change(:month => first_quarter_month)
- end
- alias :at_beginning_of_quarter :beginning_of_quarter
-
- # Returns a new Date/DateTime representing the end of the quarter (last day of march, june, september, december; DateTime objects will have time set to 23:59:59)
- def end_of_quarter
- last_quarter_month = [3, 6, 9, 12].detect { |m| m >= month }
- beginning_of_month.change(:month => last_quarter_month).end_of_month
- end
- alias :at_end_of_quarter :end_of_quarter
-
- # Returns a new Date/DateTime representing the start of the year (1st of january; DateTime objects will have time set to 0:00)
- def beginning_of_year
- if acts_like?(:time)
- change(:month => 1, :day => 1, :hour => 0)
- else
- change(:month => 1, :day => 1)
- end
- end
- alias :at_beginning_of_year :beginning_of_year
-
- # Returns a new Time representing the end of the year (31st of december; DateTime objects will have time set to 23:59:59)
- def end_of_year
- if acts_like?(:time)
- change(:month => 12, :day => 31, :hour => 23, :min => 59, :sec => 59)
- else
- change(:month => 12, :day => 31)
- end
- end
- alias :at_end_of_year :end_of_year
-
- # Convenience method which returns a new Date/DateTime representing the time 1 day ago
- def yesterday
- self - 1
- end
-
- # Convenience method which returns a new Date/DateTime representing the time 1 day since the instance time
- def tomorrow
- self + 1
- end
end
diff --git a/activesupport/lib/active_support/core_ext/date_and_time/calculations.rb b/activesupport/lib/active_support/core_ext/date_and_time/calculations.rb
new file mode 100644
index 0000000000..e703fca7a7
--- /dev/null
+++ b/activesupport/lib/active_support/core_ext/date_and_time/calculations.rb
@@ -0,0 +1,213 @@
+module DateAndTime
+ module Calculations
+ DAYS_INTO_WEEK = {
+ :monday => 0,
+ :tuesday => 1,
+ :wednesday => 2,
+ :thursday => 3,
+ :friday => 4,
+ :saturday => 5,
+ :sunday => 6
+ }
+
+ # Returns a new date/time representing yesterday.
+ def yesterday
+ advance(:days => -1)
+ end
+
+ # Returns a new date/time representing tomorrow.
+ def tomorrow
+ advance(:days => 1)
+ end
+
+ # Returns true if the date/time is today.
+ def today?
+ to_date == ::Date.current
+ end
+
+ # Returns true if the date/time is in the past.
+ def past?
+ self < self.class.current
+ end
+
+ # Returns true if the date/time is in the future.
+ def future?
+ self > self.class.current
+ end
+
+ # Returns a new date/time the specified number of days ago.
+ def days_ago(days)
+ advance(:days => -days)
+ end
+
+ # Returns a new date/time the specified number of days in the future.
+ def days_since(days)
+ advance(:days => days)
+ end
+
+ # Returns a new date/time the specified number of weeks ago.
+ def weeks_ago(weeks)
+ advance(:weeks => -weeks)
+ end
+
+ # Returns a new date/time the specified number of weeks in the future.
+ def weeks_since(weeks)
+ advance(:weeks => weeks)
+ end
+
+ # Returns a new date/time the specified number of months ago.
+ def months_ago(months)
+ advance(:months => -months)
+ end
+
+ # Returns a new date/time the specified number of months in the future.
+ def months_since(months)
+ advance(:months => months)
+ end
+
+ # Returns a new date/time the specified number of years ago.
+ def years_ago(years)
+ advance(:years => -years)
+ end
+
+ # Returns a new date/time the specified number of years in the future.
+ def years_since(years)
+ advance(:years => years)
+ end
+
+ # Returns a new date/time at the start of the month.
+ # DateTime objects will have a time set to 0:00.
+ def beginning_of_month
+ first_hour{ change(:day => 1) }
+ end
+ alias :at_beginning_of_month :beginning_of_month
+
+ # Returns a new date/time at the start of the quarter.
+ # Example: 1st January, 1st July, 1st October.
+ # DateTime objects will have a time set to 0:00.
+ def beginning_of_quarter
+ first_quarter_month = [10, 7, 4, 1].detect { |m| m <= month }
+ beginning_of_month.change(:month => first_quarter_month)
+ end
+ alias :at_beginning_of_quarter :beginning_of_quarter
+
+ # Returns a new date/time at the end of the quarter.
+ # Example: 31st March, 30th June, 30th September.
+ # DateTIme objects will have a time set to 23:59:59.
+ def end_of_quarter
+ last_quarter_month = [3, 6, 9, 12].detect { |m| m >= month }
+ beginning_of_month.change(:month => last_quarter_month).end_of_month
+ end
+ alias :at_end_of_quarter :end_of_quarter
+
+ # Return a new date/time at the beginning of the year.
+ # Example: 1st January.
+ # DateTime objects will have a time set to 0:00.
+ def beginning_of_year
+ change(:month => 1).beginning_of_month
+ end
+ alias :at_beginning_of_year :beginning_of_year
+
+ # Returns a new date/time representing the given day in the next week.
+ # Default is :monday.
+ # DateTime objects have their time set to 0:00.
+ def next_week(day = :monday)
+ first_hour{ weeks_since(1).beginning_of_week.days_since(DAYS_INTO_WEEK[day]) }
+ end
+
+ # Short-hand for months_since(1).
+ def next_month
+ months_since(1)
+ end
+
+ # Short-hand for months_since(3)
+ def next_quarter
+ months_since(3)
+ end
+
+ # Short-hand for years_since(1).
+ def next_year
+ years_since(1)
+ end
+
+ # Returns a new date/time 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)
+ first_hour{ weeks_ago(1).beginning_of_week.days_since(DAYS_INTO_WEEK[day]) }
+ end
+ alias_method :last_week, :prev_week
+
+ # Short-hand for months_ago(1).
+ def prev_month
+ months_ago(1)
+ end
+ alias_method :last_month, :prev_month
+
+ # Short-hand for months_ago(3).
+ def prev_quarter
+ months_ago(3)
+ end
+ alias_method :last_quarter, :prev_quarter
+
+ # Short-hand for years_ago(1).
+ def prev_year
+ years_ago(1)
+ end
+ alias_method :last_year, :prev_year
+
+ # Returns the number of days to the start of the week on the given 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/time representing the start of this week on the given day.
+ # Default is :monday.
+ # DateTime objects have their time set to 0:00.
+ def beginning_of_week(start_day = :monday)
+ result = days_ago(days_to_week_start(start_day))
+ acts_like?(:time) ? result.midnight : result
+ end
+ alias :at_beginning_of_week :beginning_of_week
+ alias :monday :beginning_of_week
+
+ # Returns a new date/time representing the end of this week on the given day.
+ # Default is :monday (i.e end of Sunday).
+ # DateTime objects have their time set to 23:59:59.
+ def end_of_week(start_day = :monday)
+ last_hour{ days_since(6 - days_to_week_start(start_day)) }
+ end
+ alias :at_end_of_week :end_of_week
+ alias :sunday :end_of_week
+
+ # Returns a new date/time representing the end of the month.
+ # DateTime objects will have a time set to 23:59:59.
+ def end_of_month
+ last_day = ::Time.days_in_month(month, year)
+ last_hour{ days_since(last_day - day) }
+ end
+ alias :at_end_of_month :end_of_month
+
+ # Returns a new date/time representing the end of the year.
+ # DateTime objects will have a time set to 23:59:59.
+ def end_of_year
+ change(:month => 12).end_of_month
+ end
+ alias :at_end_of_year :end_of_year
+
+ private
+
+ def first_hour
+ result = yield
+ acts_like?(:time) ? result.change(:hour => 0) : result
+ end
+
+ def last_hour
+ result = yield
+ acts_like?(:time) ? result.end_of_day : result
+ end
+ end
+end
diff --git a/activesupport/lib/active_support/core_ext/date_time.rb b/activesupport/lib/active_support/core_ext/date_time.rb
new file mode 100644
index 0000000000..e8a27b9f38
--- /dev/null
+++ b/activesupport/lib/active_support/core_ext/date_time.rb
@@ -0,0 +1,4 @@
+require 'active_support/core_ext/date_time/acts_like'
+require 'active_support/core_ext/date_time/calculations'
+require 'active_support/core_ext/date_time/conversions'
+require 'active_support/core_ext/date_time/zones'
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 fd78044b5d..5fb19f2e6e 100644
--- a/activesupport/lib/active_support/core_ext/date_time/calculations.rb
+++ b/activesupport/lib/active_support/core_ext/date_time/calculations.rb
@@ -31,8 +31,13 @@ class DateTime
end
# Returns a new DateTime where one or more of the elements have been changed according to the +options+ parameter. The time options
- # (hour, minute, sec) reset cascadingly, so if only the hour is passed, then minute and sec is set to 0. If the hour and
- # minute is passed, then sec is set to 0.
+ # (<tt>:hour</tt>, <tt>:minute</tt>, <tt>:sec</tt>) reset cascadingly, so if only the hour is passed, then minute and sec is set to 0. If the hour and
+ # minute is passed, then sec is set to 0. The +options+ parameter takes a hash with any of these keys: <tt>:year</tt>, <tt>:month</tt>, <tt>:day</tt>,
+ # <tt>:hour</tt>, <tt>:min</tt>, <tt>:sec</tt>, <tt>:offset</tt>, <tt>:start</tt>.
+ #
+ # DateTime.new(2012, 8, 29, 22, 35, 0).change(:day => 1) # => DateTime.new(2012, 8, 1, 22, 35, 0)
+ # DateTime.new(2012, 8, 29, 22, 35, 0).change(:year => 1981, :day => 1) # => DateTime.new(1981, 8, 1, 22, 35, 0)
+ # DateTime.new(2012, 8, 29, 22, 35, 0).change(:year => 1981, :hour => 0) # => DateTime.new(1981, 8, 29, 0, 0, 0)
def change(options)
::DateTime.civil(
options.fetch(:year, year),
diff --git a/activesupport/lib/active_support/core_ext/file/atomic.rb b/activesupport/lib/active_support/core_ext/file/atomic.rb
index 9e504851e7..81beb4e85d 100644
--- a/activesupport/lib/active_support/core_ext/file/atomic.rb
+++ b/activesupport/lib/active_support/core_ext/file/atomic.rb
@@ -1,3 +1,5 @@
+require 'fileutils'
+
class File
# Write to a file atomically. Useful for situations where you don't
# want other processes or threads to see half-written files.
@@ -25,17 +27,9 @@ class File
# Get original file permissions
old_stat = stat(file_name)
rescue Errno::ENOENT
- # No old permissions, write a temp file to determine the defaults
- temp_file_name = [
- '.permissions_check',
- Thread.current.object_id,
- Process.pid,
- rand(1000000)
- ].join('.')
- check_name = join(dirname(file_name), temp_file_name)
- open(check_name, 'w') { }
- old_stat = stat(check_name)
- unlink(check_name)
+ # If not possible, probe which are the default permissions in the
+ # destination directory.
+ old_stat = probe_stat_in(dirname(file_name))
end
# Overwrite original file with temp file
@@ -45,4 +39,20 @@ class File
chown(old_stat.uid, old_stat.gid, file_name)
chmod(old_stat.mode, file_name)
end
+
+ # Private utility method.
+ def self.probe_stat_in(dir) #:nodoc:
+ basename = [
+ '.permissions_check',
+ Thread.current.object_id,
+ Process.pid,
+ rand(1000000)
+ ].join('.')
+
+ file_name = join(dir, basename)
+ FileUtils.touch(file_name)
+ stat(file_name)
+ ensure
+ FileUtils.rm_f(file_name) if file_name
+ end
end
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 e5f81078ee..0d5f3501e5 100644
--- a/activesupport/lib/active_support/core_ext/object/to_param.rb
+++ b/activesupport/lib/active_support/core_ext/object/to_param.rb
@@ -6,18 +6,21 @@ class Object
end
class NilClass
+ # Returns +self+.
def to_param
self
end
end
class TrueClass
+ # Returns +self+.
def to_param
self
end
end
class FalseClass
+ # Returns +self+.
def to_param
self
end
@@ -35,12 +38,12 @@ class Hash
# 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'}.to_param
# # => "name=David&nationality=Danish"
#
# An optional namespace can be passed to enclose the param names:
#
- # {:name => 'David', :nationality => 'Danish'}.to_param('user')
+ # {name: 'David', nationality: 'Danish'}.to_param('user')
# # => "user[name]=David&user[nationality]=Danish"
#
# The string pairs "key=value" that conform the query string
diff --git a/activesupport/lib/active_support/core_ext/object/try.rb b/activesupport/lib/active_support/core_ext/object/try.rb
index 16a799ec03..1079ddde98 100644
--- a/activesupport/lib/active_support/core_ext/object/try.rb
+++ b/activesupport/lib/active_support/core_ext/object/try.rb
@@ -5,6 +5,9 @@ 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.
#
+ # This is also true if the receiving object does not implemented the tried method. It will
+ # return +nil+ in that case as well.
+ #
# If try is called without a method to call, it will yield any given block with the object.
#
# Please also note that +try+ is defined on +Object+, therefore it won't work with
@@ -31,6 +34,16 @@ class Object
if a.empty? && block_given?
yield self
else
+ public_send(*a, &b) if respond_to?(a.first)
+ end
+ end
+
+ # Same as #try, but will raise a NoMethodError exception if the receiving is not nil and
+ # does not implemented the tried method.
+ def try!(*a, &b)
+ if a.empty? && block_given?
+ yield self
+ else
public_send(*a, &b)
end
end
@@ -50,4 +63,8 @@ class NilClass
def try(*args)
nil
end
+
+ def try!(*args)
+ nil
+ end
end
diff --git a/activesupport/lib/active_support/core_ext/string.rb b/activesupport/lib/active_support/core_ext/string.rb
index fb36262528..ad864765a3 100644
--- a/activesupport/lib/active_support/core_ext/string.rb
+++ b/activesupport/lib/active_support/core_ext/string.rb
@@ -10,3 +10,4 @@ require 'active_support/core_ext/string/output_safety'
require 'active_support/core_ext/string/exclude'
require 'active_support/core_ext/string/strip'
require 'active_support/core_ext/string/inquiry'
+require 'active_support/core_ext/string/indent'
diff --git a/activesupport/lib/active_support/core_ext/string/indent.rb b/activesupport/lib/active_support/core_ext/string/indent.rb
new file mode 100644
index 0000000000..afc3032272
--- /dev/null
+++ b/activesupport/lib/active_support/core_ext/string/indent.rb
@@ -0,0 +1,43 @@
+class String
+ # Same as +indent+, except it indents the receiver in-place.
+ #
+ # Returns the indented string, or +nil+ if there was nothing to indent.
+ def indent!(amount, indent_string=nil, indent_empty_lines=false)
+ indent_string = indent_string || self[/^[ \t]/] || ' '
+ re = indent_empty_lines ? /^/ : /^(?!$)/
+ gsub!(re, indent_string * amount)
+ end
+
+ # Indents the lines in the receiver:
+ #
+ # <<EOS.indent(2)
+ # def some_method
+ # some_code
+ # end
+ # EOS
+ # # =>
+ # def some_method
+ # some_code
+ # end
+ #
+ # The second argument, +indent_string+, specifies which indent string to
+ # use. The default is +nil+, which tells the method to make a guess by
+ # peeking at the first indented line, and fallback to a space if there is
+ # none.
+ #
+ # " foo".indent(2) # => " foo"
+ # "foo\n\t\tbar".indent(2) # => "\t\tfoo\n\t\t\t\tbar"
+ # "foo".indent(2, "\t") # => "\t\tfoo"
+ #
+ # While +indent_string+ is tipically one space or tab, it may be any string.
+ #
+ # The third argument, +indent_empty_lines+, is a flag that says whether
+ # empty lines should be indented. Default is false.
+ #
+ # "foo\n\nbar".indent(2) # => " foo\n\n bar"
+ # "foo\n\nbar".indent(2, nil, true) # => " foo\n \n bar"
+ #
+ def indent(amount, indent_string=nil, indent_empty_lines=false)
+ dup.tap {|_| _.indent!(amount, indent_string, indent_empty_lines)}
+ end
+end
diff --git a/activesupport/lib/active_support/core_ext/string/inflections.rb b/activesupport/lib/active_support/core_ext/string/inflections.rb
index efa2d43f20..6edfcd7493 100644
--- a/activesupport/lib/active_support/core_ext/string/inflections.rb
+++ b/activesupport/lib/active_support/core_ext/string/inflections.rb
@@ -13,6 +13,11 @@ class String
# the singular form will be returned if <tt>count == 1</tt>.
# For any other value of +count+ the plural will be returned.
#
+ # If the optional parameter +locale+ is specified,
+ # the word will be pluralized as a word of that language.
+ # By default, this parameter is set to <tt>:en</tt>.
+ # You must define your own inflection rules for languages other than English.
+ #
# 'post'.pluralize # => "posts"
# 'octopus'.pluralize # => "octopi"
# 'sheep'.pluralize # => "sheep"
@@ -21,15 +26,23 @@ class String
# 'CamelOctopus'.pluralize # => "CamelOctopi"
# 'apple'.pluralize(1) # => "apple"
# 'apple'.pluralize(2) # => "apples"
- def pluralize(count = nil)
+ # 'ley'.pluralize(:es) # => "leyes"
+ # 'ley'.pluralize(1, :es) # => "ley"
+ def pluralize(count = nil, locale = :en)
+ locale = count if count.is_a?(Symbol)
if count == 1
self
else
- ActiveSupport::Inflector.pluralize(self)
+ ActiveSupport::Inflector.pluralize(self, locale)
end
end
# The reverse of +pluralize+, returns the singular form of a word in a string.
+ #
+ # If the optional parameter +locale+ is specified,
+ # the word will be singularized as a word of that language.
+ # By default, this paramter is set to <tt>:en</tt>.
+ # You must define your own inflection rules for languages other than English.
#
# 'posts'.singularize # => "post"
# 'octopi'.singularize # => "octopus"
@@ -37,8 +50,9 @@ class String
# 'word'.singularize # => "word"
# 'the blue mailmen'.singularize # => "the blue mailman"
# 'CamelOctopi'.singularize # => "CamelOctopus"
- def singularize
- ActiveSupport::Inflector.singularize(self)
+ # 'leyes'.singularize(:es) # => "ley"
+ def singularize(locale = :en)
+ ActiveSupport::Inflector.singularize(self, locale)
end
# +constantize+ tries to find a declared constant with the name specified
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 5226ff0cbe..dad4b29d46 100644
--- a/activesupport/lib/active_support/core_ext/string/output_safety.rb
+++ b/activesupport/lib/active_support/core_ext/string/output_safety.rb
@@ -3,9 +3,9 @@ require 'active_support/core_ext/kernel/singleton_class'
class ERB
module Util
- HTML_ESCAPE = { '&' => '&amp;', '>' => '&gt;', '<' => '&lt;', '"' => '&quot;' }
+ HTML_ESCAPE = { '&' => '&amp;', '>' => '&gt;', '<' => '&lt;', '"' => '&quot;', "'" => '&#x27;' }
JSON_ESCAPE = { '&' => '\u0026', '>' => '\u003E', '<' => '\u003C' }
- HTML_ESCAPE_ONCE_REGEXP = /[\"><]|&(?!([a-zA-Z]+|(#\d+));)/
+ HTML_ESCAPE_ONCE_REGEXP = /["><']|&(?!([a-zA-Z]+|(#\d+));)/
JSON_ESCAPE_REGEXP = /[&"><]/
# A utility method for escaping HTML tag characters.
@@ -21,7 +21,7 @@ class ERB
if s.html_safe?
s
else
- s.encode(s.encoding, :xml => :attr)[1...-1].html_safe
+ s.gsub(/[&"'><]/, HTML_ESCAPE).html_safe
end
end
@@ -60,18 +60,11 @@ class ERB
# 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:
- #
- # <%=j @person.to_json %>
- #
def json_escape(s)
result = s.to_s.gsub(JSON_ESCAPE_REGEXP) { |special| JSON_ESCAPE[special] }
s.html_safe? ? result.html_safe : result
end
- alias j json_escape
- module_function :j
module_function :json_escape
end
end
diff --git a/activesupport/lib/active_support/core_ext/time.rb b/activesupport/lib/active_support/core_ext/time.rb
new file mode 100644
index 0000000000..32cffe237d
--- /dev/null
+++ b/activesupport/lib/active_support/core_ext/time.rb
@@ -0,0 +1,5 @@
+require 'active_support/core_ext/time/acts_like'
+require 'active_support/core_ext/time/calculations'
+require 'active_support/core_ext/time/conversions'
+require 'active_support/core_ext/time/marshal'
+require 'active_support/core_ext/time/zones'
diff --git a/activesupport/lib/active_support/core_ext/time/calculations.rb b/activesupport/lib/active_support/core_ext/time/calculations.rb
index 0a71fc117c..148f2c27f1 100644
--- a/activesupport/lib/active_support/core_ext/time/calculations.rb
+++ b/activesupport/lib/active_support/core_ext/time/calculations.rb
@@ -1,18 +1,13 @@
require 'active_support/duration'
require 'active_support/core_ext/time/conversions'
require 'active_support/time_with_zone'
+require 'active_support/core_ext/time/zones'
+require 'active_support/core_ext/date_and_time/calculations'
class Time
+ include DateAndTime::Calculations
+
COMMON_YEAR_DAYS_IN_MONTH = [nil, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31]
- DAYS_INTO_WEEK = {
- :monday => 0,
- :tuesday => 1,
- :wednesday => 2,
- :thursday => 3,
- :friday => 4,
- :saturday => 5,
- :sunday => 6
- }
class << self
# Overriding case equality method so that it returns true for ActiveSupport::TimeWithZone instances
@@ -62,29 +57,19 @@ class Time
end
end
- # Tells whether the Time object's time lies in the past
- def past?
- self < ::Time.current
- end
-
- # Tells whether the Time object's time is today
- def today?
- to_date == ::Date.current
- end
-
- # Tells whether the Time object's time lies in the future
- def future?
- self > ::Time.current
- end
-
# Seconds since midnight: Time.now.seconds_since_midnight
def seconds_since_midnight
to_i - change(:hour => 0).to_i + (usec / 1.0e+6)
end
# Returns a new Time where one or more of the elements have been changed according to the +options+ parameter. The time options
- # (hour, min, sec, usec) reset cascadingly, so if only the hour is passed, then minute, sec, and usec is set to 0. If the hour and
- # minute is passed, then sec and usec is set to 0.
+ # (<tt>:hour</tt>, <tt>:min</tt>, <tt>:sec</tt>, <tt>:usec</tt>) reset cascadingly, so if only the hour is passed, then minute, sec, and usec is set to 0.
+ # If the hour and minute is passed, then sec and usec is set to 0. The +options+ parameter takes a hash with any of these keys: <tt>:year</tt>,
+ # <tt>:month</tt>, <tt>:day</tt>, <tt>:hour</tt>, <tt>:min</tt>, <tt>:sec</tt>, <tt>:usec</tt>.
+ #
+ # Time.new(2012, 8, 29, 22, 35, 0).change(:day => 1) # => Time.new(2012, 8, 1, 22, 35, 0)
+ # Time.new(2012, 8, 29, 22, 35, 0).change(:year => 1981, :day => 1) # => Time.new(1981, 8, 1, 22, 35, 0)
+ # Time.new(2012, 8, 29, 22, 35, 0).change(:year => 1981, :hour => 0) # => Time.new(1981, 8, 29, 0, 0, 0)
def change(options)
new_year = options.fetch(:year, year)
new_month = options.fetch(:month, month)
@@ -145,116 +130,6 @@ 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)
- end
-
- # Returns a new Time representing the time a number of specified months in the future
- def months_since(months)
- advance(:months => months)
- end
-
- # Returns a new Time representing the time a number of specified years ago
- def years_ago(years)
- advance(:years => -years)
- end
-
- # Returns a new Time representing the time a number of specified years in the future
- def years_since(years)
- advance(:years => years)
- end
-
- # Short-hand for years_ago(1)
- def prev_year
- years_ago(1)
- end
- alias_method :last_year, :prev_year
-
- # Short-hand for years_since(1)
- def next_year
- years_since(1)
- end
-
- # Short-hand for months_ago(1)
- def prev_month
- months_ago(1)
- end
- alias_method :last_month, :prev_month
-
- # Short-hand for months_since(1)
- def next_month
- months_since(1)
- end
-
- # Short-hand for months_ago(3)
- def prev_quarter
- months_ago(3)
- end
- alias_method :last_quarter, :prev_quarter
-
- # Short-hand for months_since(3)
- def next_quarter
- months_since(3)
- end
-
- # 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 :at_beginning_of_week :beginning_of_week
-
- # 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 +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
- alias_method :last_week, :prev_week
-
- # Returns a new Time representing the start of the given day in next week (default is :monday).
- def next_week(day = :monday)
- since(1.week).
- beginning_of_week.
- since(DAYS_INTO_WEEK[day].day).
- change(:hour => 0)
- end
-
# Returns a new Time representing the start of the day (0:00)
def beginning_of_day
#(self - seconds_since_midnight).change(:usec => 0)
@@ -289,70 +164,6 @@ class Time
)
end
- # Returns a new Time representing the start of the month (1st of the month, 0:00)
- def beginning_of_month
- #self - ((self.mday-1).days + self.seconds_since_midnight)
- change(:day => 1, :hour => 0)
- end
- alias :at_beginning_of_month :beginning_of_month
-
- # Returns a new Time representing the end of the month (end of the last day of the month)
- def end_of_month
- #self - ((self.mday-1).days + self.seconds_since_midnight)
- last_day = ::Time.days_in_month(month, year)
- change(
- :day => last_day,
- :hour => 23,
- :min => 59,
- :sec => 59,
- :usec => Rational(999999999, 1000)
- )
- end
- alias :at_end_of_month :end_of_month
-
- # Returns a new Time representing the start of the quarter (1st of january, april, july, october, 0:00)
- def beginning_of_quarter
- first_quarter_month = [10, 7, 4, 1].detect { |m| m <= month }
- beginning_of_month.change(:month => first_quarter_month)
- end
- alias :at_beginning_of_quarter :beginning_of_quarter
-
- # Returns a new Time representing the end of the quarter (end of the last day of march, june, september, december)
- def end_of_quarter
- last_quarter_month = [3, 6, 9, 12].detect { |m| m >= month }
- beginning_of_month.change(:month => last_quarter_month).end_of_month
- end
- alias :at_end_of_quarter :end_of_quarter
-
- # Returns a new Time representing the start of the year (1st of january, 0:00)
- def beginning_of_year
- change(:month => 1, :day => 1, :hour => 0)
- end
- alias :at_beginning_of_year :beginning_of_year
-
- # Returns a new Time representing the end of the year (end of the 31st of december)
- def end_of_year
- change(
- :month => 12,
- :day => 31,
- :hour => 23,
- :min => 59,
- :sec => 59,
- :usec => Rational(999999999, 1000)
- )
- end
- alias :at_end_of_year :end_of_year
-
- # Convenience method which returns a new Time representing the time 1 day ago
- def yesterday
- advance(:days => -1)
- end
-
- # Convenience method which returns a new Time representing the time 1 day since the instance time
- def tomorrow
- advance(:days => 1)
- end
-
# Returns a Range representing the whole day of the current time.
def all_day
beginning_of_day..end_of_day
diff --git a/activesupport/lib/active_support/core_ext/time/zones.rb b/activesupport/lib/active_support/core_ext/time/zones.rb
index e48866abe3..37bc3fae24 100644
--- a/activesupport/lib/active_support/core_ext/time/zones.rb
+++ b/activesupport/lib/active_support/core_ext/time/zones.rb
@@ -1,4 +1,3 @@
-require 'active_support/core_ext/time/calculations'
require 'active_support/time_with_zone'
class Time
@@ -27,11 +26,11 @@ class Time
# around_filter :set_time_zone
#
# def set_time_zone
- # old_time_zone = Time.zone
- # Time.zone = current_user.time_zone if logged_in?
- # yield
- # ensure
- # Time.zone = old_time_zone
+ # if logged_in?
+ # Time.use_zone(current_user.time_zone) { yield }
+ # else
+ # yield
+ # end
# end
# end
def zone=(time_zone)
diff --git a/activesupport/lib/active_support/dependencies.rb b/activesupport/lib/active_support/dependencies.rb
index c9071a73d8..d6bc95a522 100644
--- a/activesupport/lib/active_support/dependencies.rb
+++ b/activesupport/lib/active_support/dependencies.rb
@@ -168,16 +168,30 @@ module ActiveSupport #:nodoc:
end
end
- def const_missing(const_name, nesting = nil)
+ def const_missing(const_name)
klass_name = name.presence || "Object"
- 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
- nesting = []
- klass_name.to_s.scan(/::|$/) { nesting.unshift $` }
- end
+ # Since Ruby does not pass the nesting at the point the unknown
+ # constant triggered the callback we cannot fully emulate constant
+ # name lookup and need to make a trade-off: we are going to assume
+ # that the nesting in the body of Foo::Bar is [Foo::Bar, Foo] even
+ # though it might not be. Counterexamples are
+ #
+ # class Foo::Bar
+ # Module.nesting # => [Foo::Bar]
+ # end
+ #
+ # or
+ #
+ # module M::N
+ # module S::T
+ # Module.nesting # => [S::T, M::N]
+ # end
+ # end
+ #
+ # for example.
+ nesting = []
+ klass_name.to_s.scan(/::|$/) { nesting.unshift $` }
# If there are multiple levels of nesting to search under, the top
# level is the one we want to report as the lookup fail.
@@ -287,13 +301,11 @@ module ActiveSupport #:nodoc:
Object.class_eval { include Loadable }
Module.class_eval { include ModuleConstMissing }
Exception.class_eval { include Blamable }
- true
end
def unhook!
ModuleConstMissing.exclude_from(Module)
Loadable.exclude_from(Object)
- true
end
def load?
@@ -319,7 +331,7 @@ module ActiveSupport #:nodoc:
def require_or_load(file_name, const_path = nil)
log_call file_name, const_path
- file_name = $1 if file_name =~ /^(.*)\.rb$/
+ file_name = $` if file_name =~ /\.rb\z/
expanded = File.expand_path(file_name)
return if loaded.include?(expanded)
@@ -363,7 +375,7 @@ module ActiveSupport #:nodoc:
# Given +path+, a filesystem path to a ruby file, return an array of constant
# paths which would cause Dependencies to attempt to load this file.
def loadable_constants_for_path(path, bases = autoload_paths)
- path = $1 if path =~ /\A(.*)\.rb\Z/
+ path = $` if path =~ /\.rb\z/
expanded_path = File.expand_path(path)
paths = []
@@ -431,7 +443,7 @@ module ActiveSupport #:nodoc:
def load_file(path, const_paths = loadable_constants_for_path(path))
log_call path, const_paths
const_paths = [const_paths].compact unless const_paths.is_a? Array
- parent_paths = const_paths.collect { |const_path| /(.*)::[^:]+\Z/ =~ const_path ? $1 : :Object }
+ parent_paths = const_paths.collect { |const_path| const_path[/.*(?=::)/] || :Object }
result = nil
newly_defined_paths = new_constants_in(*parent_paths) do
@@ -467,10 +479,17 @@ module ActiveSupport #:nodoc:
file_path = search_for_file(path_suffix)
- if file_path && ! loaded.include?(File.expand_path(file_path)) # We found a matching file to load
- require_or_load file_path
- raise LoadError, "Expected #{file_path} to define #{qualified_name}" unless from_mod.const_defined?(const_name, false)
- return from_mod.const_get(const_name)
+ if file_path
+ expanded = File.expand_path(file_path)
+ expanded.sub!(/\.rb\z/, '')
+
+ if loaded.include?(expanded)
+ raise "Circular dependency detected while autoloading constant #{qualified_name}"
+ else
+ require_or_load(expanded)
+ raise LoadError, "Unable to autoload constant #{qualified_name}, expected #{file_path} to define it" unless from_mod.const_defined?(const_name, false)
+ return from_mod.const_get(const_name)
+ end
elsif mod = autoload_module!(from_mod, const_name, qualified_name, path_suffix)
return mod
elsif (parent = from_mod.parent) && parent != from_mod &&
diff --git a/activesupport/lib/active_support/dependencies/autoload.rb b/activesupport/lib/active_support/dependencies/autoload.rb
index 4045db3232..df490ae298 100644
--- a/activesupport/lib/active_support/dependencies/autoload.rb
+++ b/activesupport/lib/active_support/dependencies/autoload.rb
@@ -1,52 +1,78 @@
require "active_support/inflector/methods"
module ActiveSupport
+ # Autoload and eager load conveniences for your library.
+ #
+ # This module allows you to define autoloads based on
+ # Rails conventions (i.e. no need to define the path
+ # it is automatically guessed based on the filename)
+ # and also define a set of constants that needs to be
+ # eager loaded:
+ #
+ # module MyLib
+ # extend ActiveSupport::Autoload
+ #
+ # autoload :Model
+ #
+ # eager_autoload do
+ # autoload :Cache
+ # end
+ # end
+ #
+ # Then your library can be eager loaded by simply calling:
+ #
+ # MyLib.eager_load!
+ #
module Autoload
- @@autoloads = {}
- @@under_path = nil
- @@at_path = nil
- @@eager_autoload = false
+ def self.extended(base)
+ base.class_eval do
+ @_autoloads = {}
+ @_under_path = nil
+ @_at_path = nil
+ @_eager_autoload = false
+ end
+ end
- def autoload(const_name, path = @@at_path)
+ def autoload(const_name, path = @_at_path)
unless path
- full = [name, @@under_path, const_name.to_s, path].compact.join("::")
+ full = [name, @_under_path, const_name.to_s, path].compact.join("::")
path = Inflector.underscore(full)
end
- if @@eager_autoload
- @@autoloads[const_name] = path
+ if @_eager_autoload
+ @_autoloads[const_name] = path
end
super const_name, path
end
def autoload_under(path)
- @@under_path, old_path = path, @@under_path
+ @_under_path, old_path = path, @_under_path
yield
ensure
- @@under_path = old_path
+ @_under_path = old_path
end
def autoload_at(path)
- @@at_path, old_path = path, @@at_path
+ @_at_path, old_path = path, @_at_path
yield
ensure
- @@at_path = old_path
+ @_at_path = old_path
end
def eager_autoload
- old_eager, @@eager_autoload = @@eager_autoload, true
+ old_eager, @_eager_autoload = @_eager_autoload, true
yield
ensure
- @@eager_autoload = old_eager
+ @_eager_autoload = old_eager
end
- def self.eager_autoload!
- @@autoloads.values.each { |file| require file }
+ def eager_load!
+ @_autoloads.values.each { |file| require file }
end
def autoloads
- @@autoloads
+ @_autoloads
end
end
end
diff --git a/activesupport/lib/active_support/duration.rb b/activesupport/lib/active_support/duration.rb
index 2cdc991120..a0139b7d8e 100644
--- a/activesupport/lib/active_support/duration.rb
+++ b/activesupport/lib/active_support/duration.rb
@@ -70,7 +70,7 @@ module ActiveSupport
alias :until :ago
def inspect #:nodoc:
- consolidated = parts.inject(::Hash.new(0)) { |h,part| h[part.first] += part.last; h }
+ consolidated = parts.inject(::Hash.new(0)) { |h,(l,r)| h[l] += r; h }
parts = [:years, :months, :days, :minutes, :seconds].map do |length|
n = consolidated[length]
"#{n} #{n == 1 ? length.to_s.singularize : length.to_s}" if n.nonzero?
diff --git a/activesupport/lib/active_support/hash_with_indifferent_access.rb b/activesupport/lib/active_support/hash_with_indifferent_access.rb
index 3e6c8893e9..71713644a7 100644
--- a/activesupport/lib/active_support/hash_with_indifferent_access.rb
+++ b/activesupport/lib/active_support/hash_with_indifferent_access.rb
@@ -1,12 +1,46 @@
require 'active_support/core_ext/hash/keys'
module ActiveSupport
- # This class has dubious semantics and we only have it so that
- # people can write <tt>params[:key]</tt> instead of <tt>params['key']</tt>
- # and they get the same value for both keys.
+ # Implements a hash where keys <tt>:foo</tt> and <tt>"foo"</tt> are considered to be the same.
+ #
+ # rgb = ActiveSupport::HashWithIndifferentAccess.new
+ #
+ # rgb[:black] = '#000000'
+ # rgb[:black] # => '#000000'
+ # rgb['black'] # => '#000000'
+ #
+ # rgb['white'] = '#FFFFFF'
+ # rgb[:white] # => '#FFFFFF'
+ # rgb['white'] # => '#FFFFFF'
+ #
+ # Internally symbols are mapped to strings when used as keys in the entire
+ # writing interface (calling <tt>[]=</tt>, <tt>merge</tt>, etc). This
+ # mapping belongs to the public interface. For example, given
+ #
+ # hash = ActiveSupport::HashWithIndifferentAccess.new(:a => 1)
+ #
+ # you are guaranteed that the key is returned as a string:
+ #
+ # hash.keys # => ["a"]
+ #
+ # Technically other types of keys are accepted:
+ #
+ # hash = ActiveSupport::HashWithIndifferentAccess.new(:a => 1)
+ # hash[0] = 0
+ # hash # => {"a"=>1, 0=>0}
+ #
+ # but this class is intended for use cases where strings or symbols are the
+ # expected keys and it is convenient to understand both as the same. For
+ # example the +params+ hash in Ruby on Rails.
+ #
+ # Note that core extensions define <tt>Hash#with_indifferent_access</tt>:
+ #
+ # rgb = {:black => '#000000', :white => '#FFFFFF'}.with_indifferent_access
+ #
+ # which may be handy.
class HashWithIndifferentAccess < Hash
-
- # Always returns true, so that <tt>Array#extract_options!</tt> finds members of this class.
+ # Returns true so that <tt>Array#extract_options!</tt> finds members of
+ # this class.
def extractable_options?
true
end
@@ -51,30 +85,52 @@ module ActiveSupport
# Assigns a new value to the hash:
#
- # hash = HashWithIndifferentAccess.new
+ # hash = ActiveSupport::HashWithIndifferentAccess.new
# hash[:key] = "value"
#
+ # This value can be later fetched using either +:key+ or +"key"+.
def []=(key, value)
regular_writer(convert_key(key), convert_value(value))
end
alias_method :store, :[]=
- # Updates the instantized hash with values from the second:
+ # Updates the receiver in-place, merging in the hash passed as argument:
#
- # hash_1 = HashWithIndifferentAccess.new
+ # hash_1 = ActiveSupport::HashWithIndifferentAccess.new
# hash_1[:key] = "value"
#
- # hash_2 = HashWithIndifferentAccess.new
+ # hash_2 = ActiveSupport::HashWithIndifferentAccess.new
# hash_2[:key] = "New Value!"
#
# hash_1.update(hash_2) # => {"key"=>"New Value!"}
#
+ # The argument can be either an
+ # <tt>ActiveSupport::HashWithIndifferentAccess</tt> or a regular +Hash+.
+ # In either case the merge respects the semantics of indifferent access.
+ #
+ # If the argument is a regular hash with keys +:key+ and +"key"+ only one
+ # of the values end up in the receiver, but which one is unspecified.
+ #
+ # When given a block, the value for duplicated keys will be determined
+ # by the result of invoking the block with the duplicated key, the value
+ # in the receiver, and the value in +other_hash+. The rules for duplicated
+ # keys follow the semantics of indifferent access:
+ #
+ # hash_1[:key] = 10
+ # hash_2['key'] = 12
+ # hash_1.update(hash_2) { |key, old, new| old + new } # => {"key"=>22}
+ #
def update(other_hash)
if other_hash.is_a? HashWithIndifferentAccess
super(other_hash)
else
- other_hash.each_pair { |key, value| regular_writer(convert_key(key), convert_value(value)) }
+ other_hash.each_pair do |key, value|
+ if block_given? && key?(key)
+ value = yield(convert_key(key), self[key], value)
+ end
+ regular_writer(convert_key(key), convert_value(value))
+ end
self
end
end
@@ -83,10 +139,10 @@ module ActiveSupport
# Checks the hash for a key matching the argument passed in:
#
- # hash = HashWithIndifferentAccess.new
+ # hash = ActiveSupport::HashWithIndifferentAccess.new
# hash["key"] = "value"
- # hash.key? :key # => true
- # hash.key? "key" # => true
+ # hash.key?(:key) # => true
+ # hash.key?("key") # => true
#
def key?(key)
super(convert_key(key))
@@ -96,14 +152,24 @@ module ActiveSupport
alias_method :has_key?, :key?
alias_method :member?, :key?
- # Fetches the value for the specified key, same as doing hash[key]
+ # Same as <tt>Hash#fetch</tt> where the key passed as argument can be
+ # either a string or a symbol:
+ #
+ # counters = ActiveSupport::HashWithIndifferentAccess.new
+ # counters[:foo] = 1
+ #
+ # counters.fetch("foo") # => 1
+ # counters.fetch(:bar, 0) # => 0
+ # counters.fetch(:bar) {|key| 0} # => 0
+ # counters.fetch(:zoo) # => KeyError: key not found: "zoo"
+ #
def fetch(key, *extras)
super(convert_key(key), *extras)
end
# Returns an array of the values at the specified indices:
#
- # hash = HashWithIndifferentAccess.new
+ # hash = ActiveSupport::HashWithIndifferentAccess.new
# hash[:a] = "x"
# hash[:b] = "y"
# hash.values_at("a", "b") # => ["x", "y"]
@@ -119,23 +185,30 @@ module ActiveSupport
end
end
- # Merges the instantized and the specified hashes together, giving precedence to the values from the second hash.
- # Does not overwrite the existing hash.
- def merge(hash)
- self.dup.update(hash)
+ # This method has the same semantics of +update+, except it does not
+ # modify the receiver but rather returns a new hash with indifferent
+ # access with the result of the merge.
+ def merge(hash, &block)
+ self.dup.update(hash, &block)
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 <tt>HashWithDifferentAccess</tt>.
+ # Like +merge+ but the other way around: Merges the receiver into the
+ # argument and returns a new hash with indifferent access as result:
+ #
+ # hash = ActiveSupport::HashWithIndifferentAccess.new
+ # hash['a'] = nil
+ # hash.reverse_merge(:a => 0, :b => 1) # => {"a"=>nil, "b"=>1}
+ #
def reverse_merge(other_hash)
- super self.class.new_from_hash_copying_default(other_hash)
+ super(self.class.new_from_hash_copying_default(other_hash))
end
+ # Same semantics as +reverse_merge+ but modifies the receiver in-place.
def reverse_merge!(other_hash)
replace(reverse_merge( other_hash ))
end
- # Removes a specified key from the hash.
+ # Removes the specified key from the hash.
def delete(key)
super(convert_key(key))
end
@@ -150,7 +223,7 @@ module ActiveSupport
def deep_symbolize_keys; to_hash.deep_symbolize_keys end
def to_options!; self end
- # Convert to a Hash with String keys.
+ # Convert to a regular hash with string keys.
def to_hash
Hash.new(default).merge!(self)
end
diff --git a/activesupport/lib/active_support/inflections.rb b/activesupport/lib/active_support/inflections.rb
index ca2d8cb270..ef882ebd09 100644
--- a/activesupport/lib/active_support/inflections.rb
+++ b/activesupport/lib/active_support/inflections.rb
@@ -1,7 +1,7 @@
require 'active_support/inflector/inflections'
module ActiveSupport
- Inflector.inflections do |inflect|
+ Inflector.inflections(:en) do |inflect|
inflect.plural(/$/, 's')
inflect.plural(/s$/i, 's')
inflect.plural(/^(ax|test)is$/i, '\1es')
diff --git a/activesupport/lib/active_support/inflector/inflections.rb b/activesupport/lib/active_support/inflector/inflections.rb
index c9e50a9462..091692e5a4 100644
--- a/activesupport/lib/active_support/inflector/inflections.rb
+++ b/activesupport/lib/active_support/inflector/inflections.rb
@@ -1,13 +1,15 @@
require 'active_support/core_ext/array/prepend_and_append'
+require 'active_support/i18n'
module ActiveSupport
module Inflector
extend self
# A singleton instance of this class is yielded by Inflector.inflections, which can then be used to specify additional
- # inflection rules.
+ # inflection rules. If passed an optional locale, rules for other languages can be specified. The default locale is
+ # <tt>:en</tt>. Only rules for English are provided.
#
- # ActiveSupport::Inflector.inflections do |inflect|
+ # ActiveSupport::Inflector.inflections(:en) do |inflect|
# inflect.plural /^(ox)$/i, '\1\2en'
# inflect.singular /^(ox)en/i, '\1'
#
@@ -20,8 +22,9 @@ module ActiveSupport
# pluralization and singularization rules that is runs. This guarantees that your rules run before any of the rules that may
# already have been loaded.
class Inflections
- def self.instance
- @__instance__ ||= new
+ def self.instance(locale = :en)
+ @__instance__ ||= Hash.new { |h, k| h[k] = new }
+ @__instance__[locale]
end
attr_reader :plurals, :singulars, :uncountables, :humans, :acronyms, :acronym_regex
@@ -160,16 +163,18 @@ module ActiveSupport
end
# Yields a singleton instance of Inflector::Inflections so you can specify additional
- # inflector rules.
+ # inflector rules. If passed an optional locale, rules for other languages can be specified.
+ # If not specified, defaults to <tt>:en</tt>. Only rules for English are provided.
+ #
#
- # ActiveSupport::Inflector.inflections do |inflect|
+ # ActiveSupport::Inflector.inflections(:en) do |inflect|
# inflect.uncountable "rails"
# end
- def inflections
+ def inflections(locale = :en)
if block_given?
- yield Inflections.instance
+ yield Inflections.instance(locale)
else
- Inflections.instance
+ Inflections.instance(locale)
end
end
end
diff --git a/activesupport/lib/active_support/inflector/methods.rb b/activesupport/lib/active_support/inflector/methods.rb
index c14a43de0d..44214d16fa 100644
--- a/activesupport/lib/active_support/inflector/methods.rb
+++ b/activesupport/lib/active_support/inflector/methods.rb
@@ -10,31 +10,41 @@ module ActiveSupport
#
# The Rails core team has stated patches for the inflections library will not be accepted
# in order to avoid breaking legacy applications which may be relying on errant inflections.
- # If you discover an incorrect inflection and require it for your application, you'll need
- # to correct it yourself (explained below).
+ # If you discover an incorrect inflection and require it for your application or wish to
+ # define rules for languages other than English, please correct or add them yourself (explained below).
module Inflector
extend self
# Returns the plural form of the word in the string.
#
+ # If passed an optional +locale+ parameter, the word will be
+ # pluralized using rules defined for that language. By default,
+ # this parameter is set to <tt>:en</tt>.
+ #
# "post".pluralize # => "posts"
# "octopus".pluralize # => "octopi"
# "sheep".pluralize # => "sheep"
# "words".pluralize # => "words"
# "CamelOctopus".pluralize # => "CamelOctopi"
- def pluralize(word)
- apply_inflections(word, inflections.plurals)
+ # "ley".pluralize(:es) # => "leyes"
+ def pluralize(word, locale = :en)
+ apply_inflections(word, inflections(locale).plurals)
end
# The reverse of +pluralize+, returns the singular form of a word in a string.
#
+ # If passed an optional +locale+ parameter, the word will be
+ # pluralized using rules defined for that language. By default,
+ # this parameter is set to <tt>:en</tt>.
+ #
# "posts".singularize # => "post"
# "octopi".singularize # => "octopus"
# "sheep".singularize # => "sheep"
# "word".singularize # => "word"
# "CamelOctopi".singularize # => "CamelOctopus"
- def singularize(word)
- apply_inflections(word, inflections.singulars)
+ # "leyes".singularize(:es) # => "ley"
+ def singularize(word, locale = :en)
+ apply_inflections(word, inflections(locale).singulars)
end
# By default, +camelize+ converts strings to UpperCamelCase. If the argument to +camelize+
diff --git a/activesupport/lib/active_support/json/encoding.rb b/activesupport/lib/active_support/json/encoding.rb
index c319e94bc6..1a95bd63e6 100644
--- a/activesupport/lib/active_support/json/encoding.rb
+++ b/activesupport/lib/active_support/json/encoding.rb
@@ -1,5 +1,6 @@
require 'active_support/core_ext/object/to_json'
require 'active_support/core_ext/module/delegation'
+require 'active_support/json/variable'
require 'bigdecimal'
require 'active_support/core_ext/big_decimal/conversions' # for #to_s
@@ -161,38 +162,67 @@ class Struct #:nodoc:
end
class TrueClass
- def as_json(options = nil) self end #:nodoc:
- def encode_json(encoder) to_s end #:nodoc:
+ def as_json(options = nil) #:nodoc:
+ self
+ end
+
+ def encode_json(encoder) #:nodoc:
+ to_s
+ end
end
class FalseClass
- def as_json(options = nil) self end #:nodoc:
- def encode_json(encoder) to_s end #:nodoc:
+ def as_json(options = nil) #:nodoc:
+ self
+ end
+
+ def encode_json(encoder) #:nodoc:
+ to_s
+ end
end
class NilClass
- def as_json(options = nil) self end #:nodoc:
- def encode_json(encoder) 'null' end #:nodoc:
+ def as_json(options = nil) #:nodoc:
+ self
+ end
+
+ def encode_json(encoder) #:nodoc:
+ 'null'
+ end
end
class String
- def as_json(options = nil) self end #:nodoc:
- def encode_json(encoder) encoder.escape(self) end #:nodoc:
+ def as_json(options = nil) #:nodoc:
+ self
+ end
+
+ def encode_json(encoder) #:nodoc:
+ encoder.escape(self)
+ end
end
class Symbol
- def as_json(options = nil) to_s end #:nodoc:
+ def as_json(options = nil) #:nodoc:
+ to_s
+ end
end
class Numeric
- def as_json(options = nil) self end #:nodoc:
- def encode_json(encoder) to_s end #:nodoc:
+ def as_json(options = nil) #:nodoc:
+ self
+ end
+
+ def encode_json(encoder) #:nodoc:
+ to_s
+ end
end
class Float
# Encoding Infinity or NaN to JSON should return "null". The default returns
# "Infinity" or "NaN" which breaks parsing the JSON. E.g. JSON.parse('[NaN]').
- def as_json(options = nil) finite? ? self : nil end #:nodoc:
+ def as_json(options = nil) #:nodoc:
+ finite? ? self : nil
+ end
end
class BigDecimal
@@ -216,7 +246,9 @@ class BigDecimal
end
class Regexp
- def as_json(options = nil) to_s end #:nodoc:
+ def as_json(options = nil) #:nodoc:
+ to_s
+ end
end
module Enumerable
@@ -226,7 +258,9 @@ module Enumerable
end
class Range
- def as_json(options = nil) to_s end #:nodoc:
+ def as_json(options = nil) #:nodoc:
+ to_s
+ end
end
class Array
@@ -262,7 +296,7 @@ class Hash
Hash[subset.map { |k, v| [k.to_s, encoder.as_json(v, options)] }]
end
- def encode_json(encoder)
+ def encode_json(encoder) #:nodoc:
# 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);
diff --git a/activesupport/lib/active_support/json/variable.rb b/activesupport/lib/active_support/json/variable.rb
new file mode 100644
index 0000000000..8af661a795
--- /dev/null
+++ b/activesupport/lib/active_support/json/variable.rb
@@ -0,0 +1,17 @@
+require 'active_support/deprecation'
+
+module ActiveSupport
+ module JSON
+ # Deprecated: A string that returns itself as its JSON-encoded form.
+ class Variable < String
+ def initialize(*args)
+ ActiveSupport::Deprecation.warn 'ActiveSupport::JSON::Variable is deprecated and will be removed in Rails 4.1. ' \
+ 'For your own custom JSON literals, define #as_json and #encode_json yourself.'
+ super
+ end
+
+ def as_json(options = nil) self end #:nodoc:
+ def encode_json(encoder) self end #:nodoc:
+ end
+ end
+end
diff --git a/activesupport/lib/active_support/locale/en.yml b/activesupport/lib/active_support/locale/en.yml
index 18c7d47026..f4900dc935 100644
--- a/activesupport/lib/active_support/locale/en.yml
+++ b/activesupport/lib/active_support/locale/en.yml
@@ -49,7 +49,7 @@ en:
significant: false
# If set, the zeros after the decimal separator will always be stripped (eg.: 1.200 will be 1.2)
strip_insignificant_zeros: false
-
+
# Used in NumberHelper.number_to_currency()
currency:
format:
@@ -62,7 +62,7 @@ en:
precision: 2
significant: false
strip_insignificant_zeros: false
-
+
# Used in NumberHelper.number_to_percentage()
percentage:
format:
@@ -73,7 +73,7 @@ en:
# significant: false
# strip_insignificant_zeros: false
format: "%n%"
-
+
# Used in NumberHelper.number_to_rounded()
precision:
format:
@@ -83,7 +83,7 @@ en:
# precision:
# significant: false
# strip_insignificant_zeros: false
-
+
# Used in NumberHelper.number_to_human_size() and NumberHelper.number_to_human()
human:
format:
@@ -131,5 +131,3 @@ en:
billion: Billion
trillion: Trillion
quadrillion: Quadrillion
-
- \ No newline at end of file
diff --git a/activesupport/lib/active_support/notifications/fanout.rb b/activesupport/lib/active_support/notifications/fanout.rb
index 6ffc091233..2e5bcf4639 100644
--- a/activesupport/lib/active_support/notifications/fanout.rb
+++ b/activesupport/lib/active_support/notifications/fanout.rb
@@ -59,10 +59,10 @@ module ActiveSupport
module Subscribers # :nodoc:
def self.new(pattern, listener)
- if listener.respond_to?(:call)
- subscriber = Timed.new pattern, listener
- else
+ if listener.respond_to?(:start) and listener.respond_to?(:finish)
subscriber = Evented.new pattern, listener
+ else
+ subscriber = Timed.new pattern, listener
end
unless pattern
diff --git a/activesupport/lib/active_support/number_helper.rb b/activesupport/lib/active_support/number_helper.rb
index 99f6489adb..3849f94a31 100644
--- a/activesupport/lib/active_support/number_helper.rb
+++ b/activesupport/lib/active_support/number_helper.rb
@@ -7,12 +7,108 @@ module ActiveSupport
module NumberHelper
extend self
+ DEFAULTS = {
+ # Used in number_to_delimited
+ # These are also the defaults for 'currency', 'percentage', 'precision', and 'human'
+ format: {
+ # Sets the separator between the units, for more precision (e.g. 1.0 / 2.0 == 0.5)
+ separator: ".",
+ # Delimits thousands (e.g. 1,000,000 is a million) (always in groups of three)
+ delimiter: ",",
+ # Number of decimals, behind the separator (the number 1 with a precision of 2 gives: 1.00)
+ precision: 3,
+ # If set to true, precision will mean the number of significant digits instead
+ # of the number of decimal digits (1234 with precision 2 becomes 1200, 1.23543 becomes 1.2)
+ significant: false,
+ # If set, the zeros after the decimal separator will always be stripped (eg.: 1.200 will be 1.2)
+ strip_insignificant_zeros: false
+ },
+
+ # Used in number_to_currency
+ currency: {
+ format: {
+ format: "%u%n",
+ negative_format: "-%u%n",
+ unit: "$",
+ # These five are to override number.format and are optional
+ separator: ".",
+ delimiter: ",",
+ precision: 2,
+ significant: false,
+ strip_insignificant_zeros: false
+ }
+ },
+
+ # Used in number_to_percentage
+ percentage: {
+ format: {
+ delimiter: "",
+ format: "%n%"
+ }
+ },
+
+ # Used in number_to_rounded
+ precision: {
+ format: {
+ delimiter: ""
+ }
+ },
+
+ # Used in number_to_human_size and number_to_human
+ human: {
+ format: {
+ # These five are to override number.format and are optional
+ delimiter: "",
+ precision: 3,
+ significant: true,
+ strip_insignificant_zeros: true
+ },
+ # Used in number_to_human_size
+ storage_units: {
+ # Storage units output formatting.
+ # %u is the storage unit, %n is the number (default: 2 MB)
+ format: "%n %u",
+ units: {
+ byte: "Bytes",
+ kb: "KB",
+ mb: "MB",
+ gb: "GB",
+ tb: "TB"
+ }
+ },
+ # Used in number_to_human
+ decimal_units: {
+ format: "%n %u",
+ # Decimal units output formatting
+ # By default we will only quantify some of the exponents
+ # but the commented ones might be defined or overridden
+ # by the user.
+ units: {
+ # femto: Quadrillionth
+ # pico: Trillionth
+ # nano: Billionth
+ # micro: Millionth
+ # mili: Thousandth
+ # centi: Hundredth
+ # deci: Tenth
+ unit: "",
+ # ten:
+ # one: Ten
+ # other: Tens
+ # hundred: Hundred
+ thousand: "Thousand",
+ million: "Million",
+ billion: "Billion",
+ trillion: "Trillion",
+ quadrillion: "Quadrillion"
+ }
+ }
+ }
+ }
+
DECIMAL_UNITS = { 0 => :unit, 1 => :ten, 2 => :hundred, 3 => :thousand, 6 => :million, 9 => :billion, 12 => :trillion, 15 => :quadrillion,
-1 => :deci, -2 => :centi, -3 => :mili, -6 => :micro, -9 => :nano, -12 => :pico, -15 => :femto }
- DEFAULT_CURRENCY_VALUES = { :format => "%u%n", :negative_format => "-%u%n", :unit => "$", :separator => ".", :delimiter => ",",
- :precision => 2, :significant => false, :strip_insignificant_zeros => false }
-
STORAGE_UNITS = [:byte, :kb, :mb, :gb, :tb]
# Formats a +number+ into a US phone number (e.g., (555)
@@ -106,10 +202,10 @@ module ActiveSupport
return unless number
options = options.symbolize_keys
- currency = translations_for('currency', options[:locale])
+ currency = i18n_format_options(options[:locale], :currency)
currency[:negative_format] ||= "-" + currency[:format] if currency[:format]
- defaults = DEFAULT_CURRENCY_VALUES.merge(defaults_translations(options[:locale])).merge!(currency)
+ defaults = default_format_options(:currency).merge!(currency)
defaults[:negative_format] = "-" + options[:format] if options[:format]
options = defaults.merge!(options)
@@ -160,7 +256,7 @@ module ActiveSupport
return unless number
options = options.symbolize_keys
- defaults = format_translations('percentage', options[:locale])
+ defaults = format_options(options[:locale], :percentage)
options = defaults.merge!(options)
format = options[:format] || "%n%"
@@ -197,7 +293,7 @@ module ActiveSupport
return number unless valid_float?(number)
- options = defaults_translations(options[:locale]).merge(options)
+ options = format_options(options[:locale]).merge!(options)
parts = number.to_s.to_str.split('.')
parts[0].gsub!(/(\d)(?=(\d\d\d)+(?!\d))/, "\\1#{options[:delimiter]}")
@@ -248,7 +344,7 @@ module ActiveSupport
number = Float(number)
options = options.symbolize_keys
- defaults = format_translations('precision', options[:locale])
+ defaults = format_options(options[:locale], :precision)
options = defaults.merge!(options)
precision = options.delete :precision
@@ -328,18 +424,18 @@ module ActiveSupport
return number unless valid_float?(number)
number = Float(number)
- defaults = format_translations('human', options[:locale])
+ defaults = format_options(options[:locale], :human)
options = defaults.merge!(options)
#for backwards compatibility with those that didn't add strip_insignificant_zeros to their locale files
options[:strip_insignificant_zeros] = true if not options.key?(:strip_insignificant_zeros)
- storage_units_format = I18n.translate(:'number.human.storage_units.format', :locale => options[:locale], :raise => true)
+ storage_units_format = translate_number_value_with_default('human.storage_units.format', :locale => options[:locale], :raise => true)
base = options[:prefix] == :si ? 1000 : 1024
if number.to_i < base
- unit = I18n.translate(:'number.human.storage_units.units.byte', :locale => options[:locale], :count => number.to_i, :raise => true)
+ unit = translate_number_value_with_default('human.storage_units.units.byte', :locale => options[:locale], :count => number.to_i, :raise => true)
storage_units_format.gsub(/%n/, number.to_i.to_s).gsub(/%u/, unit)
else
max_exp = STORAGE_UNITS.size - 1
@@ -348,7 +444,7 @@ module ActiveSupport
number /= base ** exponent
unit_key = STORAGE_UNITS[exponent]
- unit = I18n.translate(:"number.human.storage_units.units.#{unit_key}", :locale => options[:locale], :count => number, :raise => true)
+ unit = translate_number_value_with_default("human.storage_units.units.#{unit_key}", :locale => options[:locale], :count => number, :raise => true)
formatted_number = self.number_to_rounded(number, options)
storage_units_format.gsub(/%n/, formatted_number).gsub(/%u/, unit)
@@ -458,7 +554,7 @@ module ActiveSupport
return number unless valid_float?(number)
number = Float(number)
- defaults = format_translations('human', options[:locale])
+ defaults = format_options(options[:locale], :human)
options = defaults.merge!(options)
#for backwards compatibility with those that didn't add strip_insignificant_zeros to their locale files
@@ -473,7 +569,7 @@ module ActiveSupport
when String, Symbol
I18n.translate(:"#{units}", :locale => options[:locale], :raise => true)
when nil
- I18n.translate(:"number.human.decimal_units.units", :locale => options[:locale], :raise => true)
+ translate_number_value_with_default("human.decimal_units.units", :locale => options[:locale], :raise => true)
else
raise ArgumentError, ":units must be a Hash or String translation scope."
end.keys.map{|e_name| inverted_du[e_name] }.sort_by{|e| -e}
@@ -488,34 +584,47 @@ module ActiveSupport
when String, Symbol
I18n.translate(:"#{units}.#{DECIMAL_UNITS[display_exponent]}", :locale => options[:locale], :count => number.to_i)
else
- I18n.translate(:"number.human.decimal_units.units.#{DECIMAL_UNITS[display_exponent]}", :locale => options[:locale], :count => number.to_i)
+ translate_number_value_with_default("human.decimal_units.units.#{DECIMAL_UNITS[display_exponent]}", :locale => options[:locale], :count => number.to_i)
end
- decimal_format = options[:format] || I18n.translate(:'number.human.decimal_units.format', :locale => options[:locale], :default => "%n %u")
+ decimal_format = options[:format] || translate_number_value_with_default('human.decimal_units.format', :locale => options[:locale])
formatted_number = self.number_to_rounded(number, options)
decimal_format.gsub(/%n/, formatted_number).gsub(/%u/, unit).strip
end
- def self.private_module_and_instance_method(method_name)
+ def self.private_module_and_instance_method(method_name) #:nodoc:
private method_name
private_class_method method_name
end
private_class_method :private_module_and_instance_method
- def format_translations(namespace, locale) #:nodoc:
- defaults_translations(locale).merge(translations_for(namespace, locale))
+ def format_options(locale, namespace = nil) #:nodoc:
+ default_format_options(namespace).merge!(i18n_format_options(locale, namespace))
+ end
+ private_module_and_instance_method :format_options
+
+ def default_format_options(namespace = nil) #:nodoc:
+ options = DEFAULTS[:format].dup
+ options.merge!(DEFAULTS[namespace][:format]) if namespace
+ options
end
- private_module_and_instance_method :format_translations
+ private_module_and_instance_method :default_format_options
- def defaults_translations(locale) #:nodoc:
- I18n.translate(:'number.format', :locale => locale, :default => {})
+ def i18n_format_options(locale, namespace = nil) #:nodoc:
+ options = I18n.translate(:'number.format', locale: locale, default: {}).dup
+ if namespace
+ options.merge!(I18n.translate(:"number.#{namespace}.format", locale: locale, default: {}))
+ end
+ options
end
- private_module_and_instance_method :defaults_translations
+ private_module_and_instance_method :i18n_format_options
+
+ def translate_number_value_with_default(key, i18n_options = {}) #:nodoc:
+ default = key.split('.').reduce(DEFAULTS) { |defaults, k| defaults[k.to_sym] }
- def translations_for(namespace, locale) #:nodoc:
- I18n.translate(:"number.#{namespace}.format", :locale => locale, :default => {})
+ I18n.translate(key, { default: default, scope: :number }.merge!(i18n_options))
end
- private_module_and_instance_method :translations_for
+ private_module_and_instance_method :translate_number_value_with_default
def valid_float?(number) #:nodoc:
Float(number)
diff --git a/activesupport/lib/active_support/rails.rb b/activesupport/lib/active_support/rails.rb
new file mode 100644
index 0000000000..b05c3ff126
--- /dev/null
+++ b/activesupport/lib/active_support/rails.rb
@@ -0,0 +1,27 @@
+# This is private interface.
+#
+# Rails components cherry pick from Active Support as needed, but there are a
+# few features that are used for sure some way or another and it is not worth
+# to put individual requires absolutely everywhere. Think blank? for example.
+#
+# This file is loaded by every Rails component except Active Support itself,
+# but it does not belong to the Rails public interface. It is internal to
+# Rails and can change anytime.
+
+# Defines Object#blank? and Object#present?.
+require 'active_support/core_ext/object/blank'
+
+# Rails own autoload, eager_load, etc.
+require 'active_support/dependencies/autoload'
+
+# Support for ClassMethods and the included macro.
+require 'active_support/concern'
+
+# Defines Class#class_attribute.
+require 'active_support/core_ext/class/attribute'
+
+# Defines Module#delegate.
+require 'active_support/core_ext/module/delegation'
+
+# Defines ActiveSupport::Deprecation.
+require 'active_support/deprecation'
diff --git a/activesupport/lib/active_support/railtie.rb b/activesupport/lib/active_support/railtie.rb
index 30ac881090..aa8d408da9 100644
--- a/activesupport/lib/active_support/railtie.rb
+++ b/activesupport/lib/active_support/railtie.rb
@@ -5,6 +5,8 @@ module ActiveSupport
class Railtie < Rails::Railtie
config.active_support = ActiveSupport::OrderedOptions.new
+ config.eager_load_namespaces << ActiveSupport
+
initializer "active_support.deprecation_behavior" do |app|
if deprecation = app.config.active_support.deprecation
ActiveSupport::Deprecation.behavior = deprecation
diff --git a/activesupport/lib/active_support/string_inquirer.rb b/activesupport/lib/active_support/string_inquirer.rb
index f3f3909a90..5f20bfa7bc 100644
--- a/activesupport/lib/active_support/string_inquirer.rb
+++ b/activesupport/lib/active_support/string_inquirer.rb
@@ -10,12 +10,18 @@ module ActiveSupport
# Rails.env.production?
#
class StringInquirer < String
- def method_missing(method_name, *arguments)
- if method_name[-1, 1] == "?"
- self == method_name[0..-2]
- else
- super
+ private
+
+ def respond_to_missing?(method_name, include_private = false)
+ method_name[-1] == '?'
+ end
+
+ def method_missing(method_name, *arguments)
+ if method_name[-1] == '?'
+ self == method_name[0..-2]
+ else
+ super
+ end
end
- end
end
end
diff --git a/activesupport/lib/active_support/test_case.rb b/activesupport/lib/active_support/test_case.rb
index a6f3b43792..d2b8e602f9 100644
--- a/activesupport/lib/active_support/test_case.rb
+++ b/activesupport/lib/active_support/test_case.rb
@@ -3,25 +3,19 @@ require 'minitest/spec'
require 'active_support/testing/setup_and_teardown'
require 'active_support/testing/assertions'
require 'active_support/testing/deprecation'
-require 'active_support/testing/declarative'
require 'active_support/testing/isolation'
require 'active_support/testing/mocha_module'
require 'active_support/core_ext/kernel/reporting'
+require 'active_support/deprecation'
module ActiveSupport
class TestCase < ::MiniTest::Spec
include ActiveSupport::Testing::MochaModule
- if MiniTest::Unit::VERSION < '2.6.1'
- class << self
- alias :name :to_s
- end
- end
-
# Use AS::TestCase for the base class when describing a model
register_spec_type(self) do |desc|
- desc < ActiveRecord::Model
+ Class === desc && desc < ActiveRecord::Model
end
Assertion = MiniTest::Assertion
@@ -41,7 +35,24 @@ module ActiveSupport
include ActiveSupport::Testing::SetupAndTeardown
include ActiveSupport::Testing::Assertions
include ActiveSupport::Testing::Deprecation
- extend ActiveSupport::Testing::Declarative
+
+ def self.describe(text)
+ if block_given?
+ super
+ else
+ ActiveSupport::Deprecation.warn("`describe` without a block is deprecated, please switch to: `def self.name; #{text.inspect}; end`\n")
+
+ class_eval <<-RUBY_EVAL, __FILE__, __LINE__ + 1
+ def self.name
+ "#{text}"
+ end
+ RUBY_EVAL
+ end
+ end
+
+ class << self
+ alias :test :it
+ end
# test/unit backwards compatibility methods
alias :assert_raise :assert_raises
diff --git a/activesupport/lib/active_support/testing/declarative.rb b/activesupport/lib/active_support/testing/declarative.rb
deleted file mode 100644
index 508e37254a..0000000000
--- a/activesupport/lib/active_support/testing/declarative.rb
+++ /dev/null
@@ -1,40 +0,0 @@
-module ActiveSupport
- module Testing
- module Declarative
-
- def self.extended(klass) #:nodoc:
- klass.class_eval do
-
- unless method_defined?(:describe)
- def self.describe(text)
- class_eval <<-RUBY_EVAL, __FILE__, __LINE__ + 1
- def self.name
- "#{text}"
- end
- RUBY_EVAL
- end
- end
-
- end
- end
-
- unless defined?(Spec)
- # test "verify something" do
- # ...
- # end
- def test(name, &block)
- test_name = "test_#{name.gsub(/\s+/,'_')}".to_sym
- defined = instance_method(test_name) rescue false
- raise "#{test_name} is already defined in #{self}" if defined
- if block_given?
- define_method(test_name, &block)
- else
- define_method(test_name) do
- flunk "No implementation provided for #{name}"
- end
- end
- end
- end
- end
- end
-end
diff --git a/activesupport/lib/active_support/time_with_zone.rb b/activesupport/lib/active_support/time_with_zone.rb
index 451520ac5c..93c2d614f5 100644
--- a/activesupport/lib/active_support/time_with_zone.rb
+++ b/activesupport/lib/active_support/time_with_zone.rb
@@ -1,6 +1,5 @@
require 'active_support/values/time_zone'
require 'active_support/core_ext/object/acts_like'
-require 'active_support/core_ext/object/inclusion'
module ActiveSupport
# A Time-like class that can represent a time in any time zone. Necessary because standard Ruby Time instances are
@@ -339,7 +338,7 @@ module ActiveSupport
end
def duration_of_variable_length?(obj)
- ActiveSupport::Duration === obj && obj.parts.any? {|p| p[0].in?([:years, :months, :days]) }
+ ActiveSupport::Duration === obj && obj.parts.any? {|p| [:years, :months, :days].include?(p[0]) }
end
def wrap_with_time_zone(time)