diff options
Diffstat (limited to 'activesupport/lib/active_support')
6 files changed, 255 insertions, 399 deletions
diff --git a/activesupport/lib/active_support/core_ext/date/calculations.rb b/activesupport/lib/active_support/core_ext/date/calculations.rb index 31726962e3..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) @@ -117,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/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/time/calculations.rb b/activesupport/lib/active_support/core_ext/time/calculations.rb index 0eb6e9e61d..148f2c27f1 100644 --- a/activesupport/lib/active_support/core_ext/time/calculations.rb +++ b/activesupport/lib/active_support/core_ext/time/calculations.rb @@ -2,18 +2,12 @@ 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 @@ -63,21 +57,6 @@ 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) @@ -151,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) @@ -295,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/dependencies.rb b/activesupport/lib/active_support/dependencies.rb index 77cab6f08d..45c9f0472a 100644 --- a/activesupport/lib/active_support/dependencies.rb +++ b/activesupport/lib/active_support/dependencies.rb @@ -331,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) @@ -375,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 = [] @@ -443,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 @@ -479,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, "Expected #{file_path} to define #{qualified_name}" 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/tagged_logging.rb b/activesupport/lib/active_support/tagged_logging.rb index 9cd8ac36bc..5e080df518 100644 --- a/activesupport/lib/active_support/tagged_logging.rb +++ b/activesupport/lib/active_support/tagged_logging.rb @@ -16,7 +16,7 @@ module ActiveSupport module Formatter # :nodoc: # This method is invoked when a log event occurs def call(severity, timestamp, progname, msg) - super(severity, timestamp, progname, "#{tags_text} #{msg}".lstrip) + super(severity, timestamp, progname, "#{tags_text}#{msg}") end def clear! @@ -31,7 +31,7 @@ module ActiveSupport def tags_text tags = current_tags if tags.any? - tags.collect { |tag| "[#{tag}]" }.join(' ') + tags.collect { |tag| "[#{tag}] " }.join end end end |