diff options
author | rick <technoweenie@gmail.com> | 2008-09-20 13:00:20 -0700 |
---|---|---|
committer | rick <technoweenie@gmail.com> | 2008-09-20 13:00:20 -0700 |
commit | 22e830f883af0b56de81186c184751b6398d0141 (patch) | |
tree | 0de20fad9f3a7ce2e49d660d1243b5b02a32e290 /activesupport | |
parent | 0aef9d1a2651fa0acd2adcd2de308eeb0ec8cdd2 (diff) | |
parent | a3b7fa78bfdc33e45e39c095b67e02d50a2c7bea (diff) | |
download | rails-22e830f883af0b56de81186c184751b6398d0141.tar.gz rails-22e830f883af0b56de81186c184751b6398d0141.tar.bz2 rails-22e830f883af0b56de81186c184751b6398d0141.zip |
Merge branch 'master' of git@github.com:rails/rails
Diffstat (limited to 'activesupport')
63 files changed, 1500 insertions, 533 deletions
diff --git a/activesupport/CHANGELOG b/activesupport/CHANGELOG index 8d3b136d80..00da2a2284 100644 --- a/activesupport/CHANGELOG +++ b/activesupport/CHANGELOG @@ -1,5 +1,17 @@ *Edge* +* TimeWithZone #wday, #yday and #to_date avoid trip through #method_missing [Geoff Buesing] + +* Added Time, Date, DateTime and TimeWithZone #past?, #future? and #today? #720 [Clemens Kofler, Geoff Buesing] + +* Fixed Sri Jayawardenepura time zone to map to Asia/Colombo [Jamis Buck] + +* Added Inflector#parameterize for easy slug generation ("Donald E. Knuth".parameterize => "donald-e-knuth") #713 [Matt Darby] + +* Changed cache benchmarking to be reported in milliseconds [DHH] + +* Fix Ruby's Time marshaling bug in pre-1.9 versions of Ruby: utc instances are now correctly unmarshaled with a utc zone instead of the system local zone [#900 state:resolved] [Luca Guidi, Geoff Buesing] + * Add Array#in_groups which splits or iterates over the array in specified number of groups. #579. [Adrian Mugnolo] Example: a = (1..10).to_a diff --git a/activesupport/lib/active_support.rb b/activesupport/lib/active_support.rb index ae3e2360cd..b30faff06d 100644 --- a/activesupport/lib/active_support.rb +++ b/activesupport/lib/active_support.rb @@ -28,7 +28,6 @@ require 'active_support/callbacks' require 'active_support/core_ext' -require 'active_support/clean_logger' require 'active_support/buffered_logger' require 'active_support/gzip' @@ -55,9 +54,9 @@ require 'active_support/base64' require 'active_support/time_with_zone' -I18n.populate do - I18n.load_translations File.dirname(__FILE__) + '/active_support/locale/en-US.yml' -end +require 'active_support/secure_random' + +I18n.load_path << File.dirname(__FILE__) + '/active_support/locale/en-US.yml' Inflector = ActiveSupport::Deprecation::DeprecatedConstantProxy.new('Inflector', 'ActiveSupport::Inflector') Dependencies = ActiveSupport::Deprecation::DeprecatedConstantProxy.new('Dependencies', 'ActiveSupport::Dependencies') diff --git a/activesupport/lib/active_support/buffered_logger.rb b/activesupport/lib/active_support/buffered_logger.rb index 6553d72b4f..77e0b1d33f 100644 --- a/activesupport/lib/active_support/buffered_logger.rb +++ b/activesupport/lib/active_support/buffered_logger.rb @@ -116,7 +116,7 @@ module ActiveSupport end def clear_buffer - @buffer[Thread.current] = [] + @buffer.delete(Thread.current) end end end diff --git a/activesupport/lib/active_support/cache.rb b/activesupport/lib/active_support/cache.rb index 95eae3a77e..51a6309dce 100644 --- a/activesupport/lib/active_support/cache.rb +++ b/activesupport/lib/active_support/cache.rb @@ -62,7 +62,7 @@ module ActiveSupport write(key, value, options) @logger_off = false - log("write (will save #{'%.5f' % seconds})", key, nil) + log("write (will save #{'%.2f' % (seconds * 1000)}ms)", key, nil) value end diff --git a/activesupport/lib/active_support/cache/file_store.rb b/activesupport/lib/active_support/cache/file_store.rb index 659bde64f0..ef533633b2 100644 --- a/activesupport/lib/active_support/cache/file_store.rb +++ b/activesupport/lib/active_support/cache/file_store.rb @@ -65,6 +65,6 @@ module ActiveSupport end end end - end + end end end diff --git a/activesupport/lib/active_support/cache/memory_store.rb b/activesupport/lib/active_support/cache/memory_store.rb index f3e4b8c13b..c1a713b4c5 100644 --- a/activesupport/lib/active_support/cache/memory_store.rb +++ b/activesupport/lib/active_support/cache/memory_store.rb @@ -3,63 +3,35 @@ module ActiveSupport class MemoryStore < Store def initialize @data = {} - @guard = Monitor.new - end - - def fetch(key, options = {}) - @guard.synchronize do - super - end end def read(name, options = nil) - @guard.synchronize do - super - @data[name] - end + super + @data[name] end def write(name, value, options = nil) - @guard.synchronize do - super - @data[name] = value.freeze - end + super + @data[name] = value.freeze end def delete(name, options = nil) - @guard.synchronize do - @data.delete(name) - end + super + @data.delete(name) end def delete_matched(matcher, options = nil) - @guard.synchronize do - @data.delete_if { |k,v| k =~ matcher } - end + super + @data.delete_if { |k,v| k =~ matcher } end def exist?(name,options = nil) - @guard.synchronize do - @data.has_key?(name) - end - end - - def increment(key, amount = 1) - @guard.synchronize do - super - end - end - - def decrement(key, amount = 1) - @guard.synchronize do - super - end + super + @data.has_key?(name) end def clear - @guard.synchronize do - @data.clear - end + @data.clear end end end diff --git a/activesupport/lib/active_support/cache/synchronized_memory_store.rb b/activesupport/lib/active_support/cache/synchronized_memory_store.rb new file mode 100644 index 0000000000..d2ff28768f --- /dev/null +++ b/activesupport/lib/active_support/cache/synchronized_memory_store.rb @@ -0,0 +1,46 @@ +module ActiveSupport + module Cache + class SynchronizedMemoryStore < MemoryStore + def initialize + super + @guard = Monitor.new + end + + def fetch(key, options = {}) + @guard.synchronize { super } + end + + def read(name, options = nil) + @guard.synchronize { super } + end + + def write(name, value, options = nil) + @guard.synchronize { super } + end + + def delete(name, options = nil) + @guard.synchronize { super } + end + + def delete_matched(matcher, options = nil) + @guard.synchronize { super } + end + + def exist?(name,options = nil) + @guard.synchronize { super } + end + + def increment(key, amount = 1) + @guard.synchronize { super } + end + + def decrement(key, amount = 1) + @guard.synchronize { super } + end + + def clear + @guard.synchronize { super } + end + end + end +end diff --git a/activesupport/lib/active_support/callbacks.rb b/activesupport/lib/active_support/callbacks.rb index 7b905930bb..5cdcaf5ad1 100644 --- a/activesupport/lib/active_support/callbacks.rb +++ b/activesupport/lib/active_support/callbacks.rb @@ -188,7 +188,7 @@ module ActiveSupport "Callbacks must be a symbol denoting the method to call, a string to be evaluated, " + "a block to be invoked, or an object responding to the callback method." end - end + end end def should_run_callback?(*args) diff --git a/activesupport/lib/active_support/clean_logger.rb b/activesupport/lib/active_support/clean_logger.rb deleted file mode 100644 index b4c27ebc9d..0000000000 --- a/activesupport/lib/active_support/clean_logger.rb +++ /dev/null @@ -1,127 +0,0 @@ -require 'logger' -require 'active_support/core_ext/class/attribute_accessors' - -# Extensions to the built in Ruby logger. -# -# If you want to use the default log formatter as defined in the Ruby core, then you -# will need to set the formatter for the logger as in: -# -# logger.formatter = Formatter.new -# -# You can then specify the datetime format, for example: -# -# logger.datetime_format = "%Y-%m-%d" -# -# Note: This logger is deprecated in favor of ActiveSupport::BufferedLogger -class Logger - # Set to false to disable the silencer - cattr_accessor :silencer - self.silencer = true - - # Silences the logger for the duration of the block. - def silence(temporary_level = Logger::ERROR) - if silencer - begin - old_logger_level, self.level = level, temporary_level - yield self - ensure - self.level = old_logger_level - end - else - yield self - end - end - - alias :old_datetime_format= :datetime_format= - # Logging date-time format (string passed to +strftime+). Ignored if the formatter - # does not respond to datetime_format=. - def datetime_format=(datetime_format) - formatter.datetime_format = datetime_format if formatter.respond_to?(:datetime_format=) - end - - alias :old_datetime_format :datetime_format - # Get the logging datetime format. Returns nil if the formatter does not support - # datetime formatting. - def datetime_format - formatter.datetime_format if formatter.respond_to?(:datetime_format) - end - - alias :old_formatter :formatter if method_defined?(:formatter) - # Get the current formatter. The default formatter is a SimpleFormatter which only - # displays the log message - def formatter - @formatter ||= SimpleFormatter.new - end - - unless const_defined? :Formatter - class Formatter - Format = "%s, [%s#%d] %5s -- %s: %s\n" - - attr_accessor :datetime_format - - def initialize - @datetime_format = nil - end - - def call(severity, time, progname, msg) - Format % [severity[0..0], format_datetime(time), $$, severity, progname, - msg2str(msg)] - end - - private - def format_datetime(time) - if @datetime_format.nil? - time.strftime("%Y-%m-%dT%H:%M:%S.") << "%06d " % time.usec - else - time.strftime(@datetime_format) - end - end - - def msg2str(msg) - case msg - when ::String - msg - when ::Exception - "#{ msg.message } (#{ msg.class })\n" << - (msg.backtrace || []).join("\n") - else - msg.inspect - end - end - end - end - - # Simple formatter which only displays the message. - class SimpleFormatter < Logger::Formatter - # This method is invoked when a log event occurs - def call(severity, timestamp, progname, msg) - "#{String === msg ? msg : msg.inspect}\n" - end - end - - private - alias old_format_message format_message - - # Ruby 1.8.3 transposed the msg and progname arguments to format_message. - # We can't test RUBY_VERSION because some distributions don't keep Ruby - # and its standard library in sync, leading to installations of Ruby 1.8.2 - # with Logger from 1.8.3 and vice versa. - if method_defined?(:formatter=) - def format_message(severity, timestamp, progname, msg) - formatter.call(severity, timestamp, progname, msg) - end - else - def format_message(severity, timestamp, msg, progname) - formatter.call(severity, timestamp, progname, msg) - end - - attr_writer :formatter - public :formatter= - - alias old_format_datetime format_datetime - def format_datetime(datetime) datetime end - - alias old_msg2str msg2str - def msg2str(msg) msg end - end -end diff --git a/activesupport/lib/active_support/core_ext/array/grouping.rb b/activesupport/lib/active_support/core_ext/array/grouping.rb index dd1484f8fa..f782f8facf 100644 --- a/activesupport/lib/active_support/core_ext/array/grouping.rb +++ b/activesupport/lib/active_support/core_ext/array/grouping.rb @@ -7,16 +7,16 @@ module ActiveSupport #:nodoc: # Splits or iterates over the array in groups of size +number+, # padding any remaining slots with +fill_with+ unless it is +false+. # - # %w(1 2 3 4 5 6 7).in_groups_of(3) {|g| p g} + # %w(1 2 3 4 5 6 7).in_groups_of(3) {|group| p group} # ["1", "2", "3"] # ["4", "5", "6"] # ["7", nil, nil] # - # %w(1 2 3).in_groups_of(2, ' ') {|g| p g} + # %w(1 2 3).in_groups_of(2, ' ') {|group| p group} # ["1", "2"] # ["3", " "] # - # %w(1 2 3).in_groups_of(2, false) {|g| p g} + # %w(1 2 3).in_groups_of(2, false) {|group| p group} # ["1", "2"] # ["3"] def in_groups_of(number, fill_with = nil) @@ -42,17 +42,17 @@ module ActiveSupport #:nodoc: # Splits or iterates over the array in +number+ of groups, padding any # remaining slots with +fill_with+ unless it is +false+. # - # %w(1 2 3 4 5 6 7 8 9 10).in_groups(3) {|g| p g} + # %w(1 2 3 4 5 6 7 8 9 10).in_groups(3) {|group| p group} # ["1", "2", "3", "4"] # ["5", "6", "7", nil] # ["8", "9", "10", nil] # - # %w(1 2 3 4 5 6 7).in_groups(3, ' ') {|g| p g} + # %w(1 2 3 4 5 6 7).in_groups(3, ' ') {|group| p group} # ["1", "2", "3"] # ["4", "5", " "] # ["6", "7", " "] # - # %w(1 2 3 4 5 6 7).in_groups(3, false) {|g| p g} + # %w(1 2 3 4 5 6 7).in_groups(3, false) {|group| p group} # ["1", "2", "3"] # ["4", "5"] # ["6", "7"] diff --git a/activesupport/lib/active_support/core_ext/bigdecimal/conversions.rb b/activesupport/lib/active_support/core_ext/bigdecimal/conversions.rb index 94c7c779f7..bc9d578f38 100644 --- a/activesupport/lib/active_support/core_ext/bigdecimal/conversions.rb +++ b/activesupport/lib/active_support/core_ext/bigdecimal/conversions.rb @@ -4,35 +4,31 @@ module ActiveSupport #:nodoc: module CoreExtensions #:nodoc: module BigDecimal #:nodoc: module Conversions + DEFAULT_STRING_FORMAT = 'F'.freeze + YAML_TAG = 'tag:yaml.org,2002:float'.freeze + YAML_MAPPING = { 'Infinity' => '.Inf', '-Infinity' => '-.Inf', 'NaN' => '.NaN' } + def self.included(base) #:nodoc: - base.instance_eval do + base.class_eval do alias_method :_original_to_s, :to_s alias_method :to_s, :to_formatted_s + + yaml_as YAML_TAG end end - - def to_formatted_s(format="F") + + def to_formatted_s(format = DEFAULT_STRING_FORMAT) _original_to_s(format) end - - yaml_as "tag:yaml.org,2002:float" - def to_yaml( opts = {} ) - YAML::quick_emit( nil, opts ) do |out| - # This emits the number without any scientific notation. - # I prefer it to using self.to_f.to_s, which would lose precision. - # - # Note that YAML allows that when reconstituting floats - # to native types, some precision may get lost. - # There is no full precision real YAML tag that I am aware of. - str = self.to_s - if str == "Infinity" - str = ".Inf" - elsif str == "-Infinity" - str = "-.Inf" - elsif str == "NaN" - str = ".NaN" - end - out.scalar( "tag:yaml.org,2002:float", str, :plain ) + + # This emits the number without any scientific notation. + # This is better than self.to_f.to_s since it doesn't lose precision. + # + # Note that reconstituting YAML floats to native floats may lose precision. + def to_yaml(opts = {}) + YAML.quick_emit(nil, opts) do |out| + string = to_s + out.scalar(YAML_TAG, YAML_MAPPING[string] || string, :plain) end end end diff --git a/activesupport/lib/active_support/core_ext/class/inheritable_attributes.rb b/activesupport/lib/active_support/core_ext/class/inheritable_attributes.rb index 8724a492bf..e6143a274b 100644 --- a/activesupport/lib/active_support/core_ext/class/inheritable_attributes.rb +++ b/activesupport/lib/active_support/core_ext/class/inheritable_attributes.rb @@ -91,14 +91,14 @@ class Class # :nodoc: def inheritable_attributes @inheritable_attributes ||= EMPTY_INHERITABLE_ATTRIBUTES end - + def write_inheritable_attribute(key, value) if inheritable_attributes.equal?(EMPTY_INHERITABLE_ATTRIBUTES) @inheritable_attributes = {} end inheritable_attributes[key] = value end - + def write_inheritable_array(key, elements) write_inheritable_attribute(key, []) if read_inheritable_attribute(key).nil? write_inheritable_attribute(key, read_inheritable_attribute(key) + elements) @@ -112,7 +112,7 @@ class Class # :nodoc: def read_inheritable_attribute(key) inheritable_attributes[key] end - + def reset_inheritable_attributes @inheritable_attributes = EMPTY_INHERITABLE_ATTRIBUTES end @@ -123,7 +123,7 @@ class Class # :nodoc: def inherited_with_inheritable_attributes(child) inherited_without_inheritable_attributes(child) if respond_to?(:inherited_without_inheritable_attributes) - + if inheritable_attributes.equal?(EMPTY_INHERITABLE_ATTRIBUTES) new_inheritable_attributes = EMPTY_INHERITABLE_ATTRIBUTES else @@ -131,7 +131,7 @@ class Class # :nodoc: memo.update(key => value.duplicable? ? value.dup : value) end end - + child.instance_variable_set('@inheritable_attributes', new_inheritable_attributes) end diff --git a/activesupport/lib/active_support/core_ext/date/behavior.rb b/activesupport/lib/active_support/core_ext/date/behavior.rb index 6f8f7c6a82..bd378eb375 100644 --- a/activesupport/lib/active_support/core_ext/date/behavior.rb +++ b/activesupport/lib/active_support/core_ext/date/behavior.rb @@ -1,3 +1,5 @@ +require 'date' + module ActiveSupport #:nodoc: module CoreExtensions #:nodoc: module Date #:nodoc: @@ -10,18 +12,29 @@ module ActiveSupport #:nodoc: # Date memoizes some instance methods using metaprogramming to wrap # the methods with one that caches the result in an instance variable. + # # If a Date is frozen but the memoized method hasn't been called, the # first call will result in a frozen object error since the memo - # instance variable is uninitialized. Work around by eagerly memoizing - # before freezing. - def freeze #:nodoc: - self.class.private_instance_methods(false).each do |m| - if m.to_s =~ /\A__\d+__\Z/ - instance_variable_set(:"@#{m}", [send(m)]) + # instance variable is uninitialized. + # + # Work around by eagerly memoizing before freezing. + # + # Ruby 1.9 uses a preinitialized instance variable so it's unaffected. + # This hack is as close as we can get to feature detection: + begin + ::Date.today.freeze.jd + rescue => frozen_object_error + if frozen_object_error.message =~ /frozen/ + def freeze #:nodoc: + self.class.private_instance_methods(false).each do |m| + if m.to_s =~ /\A__\d+__\Z/ + instance_variable_set(:"@#{m}", [send(m)]) + end + end + + super end end - - super end end end diff --git a/activesupport/lib/active_support/core_ext/date/calculations.rb b/activesupport/lib/active_support/core_ext/date/calculations.rb index b5180c9592..43d70c7013 100644 --- a/activesupport/lib/active_support/core_ext/date/calculations.rb +++ b/activesupport/lib/active_support/core_ext/date/calculations.rb @@ -20,18 +20,33 @@ module ActiveSupport #:nodoc: def yesterday ::Date.today.yesterday end - + # Returns a new Date representing the date 1 day after today (i.e. tomorrow's date). def tomorrow ::Date.today.tomorrow end - + # Returns Time.zone.today when config.time_zone is set, otherwise just returns Date.today. def current ::Time.zone_default ? ::Time.zone.today : ::Date.today end end - + + # Tells whether the Date object's date lies in the past + def past? + self < ::Date.current + end + + # Tells whether the Date object's date is today + def today? + self.to_date == ::Date.current # we need the to_date because of DateTime + end + + # Tells whether 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) @@ -57,7 +72,7 @@ module ActiveSupport #:nodoc: def end_of_day to_time.end_of_day end - + def plus_with_duration(other) #:nodoc: if ActiveSupport::Duration === other other.since(self) @@ -65,7 +80,7 @@ module ActiveSupport #:nodoc: plus_without_duration(other) end end - + def minus_with_duration(other) #:nodoc: if ActiveSupport::Duration === other plus_with_duration(-other) @@ -73,8 +88,8 @@ module ActiveSupport #:nodoc: minus_without_duration(other) end end - - # Provides precise Date calculations for years, months, and days. The +options+ parameter takes a hash with + + # Provides precise Date calculations for years, months, and days. The +options+ parameter takes a hash with # any of these keys: <tt>:years</tt>, <tt>:months</tt>, <tt>:weeks</tt>, <tt>:days</tt>. def advance(options) d = self @@ -98,7 +113,7 @@ module ActiveSupport #:nodoc: options[:day] || self.day ) end - + # Returns a new Date/DateTime representing the time a number of specified months ago def months_ago(months) advance(:months => -months) @@ -161,7 +176,7 @@ module ActiveSupport #:nodoc: days_into_week = { :monday => 0, :tuesday => 1, :wednesday => 2, :thursday => 3, :friday => 4, :saturday => 5, :sunday => 6} result = (self + 7).beginning_of_week + days_into_week[day] self.acts_like?(:time) ? result.change(:hour => 0) : result - end + end # Returns a new ; DateTime objects will have time set to 0:00DateTime representing the start of the month (1st of the month; DateTime objects will have time set to 0:00) def beginning_of_month 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 155c961a91..0099431e9d 100644 --- a/activesupport/lib/active_support/core_ext/date_time/calculations.rb +++ b/activesupport/lib/active_support/core_ext/date_time/calculations.rb @@ -7,7 +7,7 @@ module ActiveSupport #:nodoc: module Calculations def self.included(base) #:nodoc: base.extend ClassMethods - + base.class_eval do alias_method :compare_without_coercion, :<=> alias_method :<=>, :compare_with_coercion @@ -19,6 +19,20 @@ module ActiveSupport #:nodoc: def local_offset ::Time.local(2007).utc_offset.to_r / 86400 end + + def current + ::Time.zone_default ? ::Time.zone.now.to_datetime : ::Time.now.to_datetime + end + end + + # Tells whether the DateTime object's datetime lies in the past + def past? + self < ::DateTime.current + end + + # Tells whether the DateTime object's datetime lies in the future + def future? + self > ::DateTime.current end # Seconds since midnight: DateTime.now.seconds_since_midnight @@ -78,7 +92,7 @@ module ActiveSupport #:nodoc: def end_of_day change(:hour => 23, :min => 59, :sec => 59) end - + # Adjusts DateTime to UTC by adding its offset value; offset is set to 0 # # Example: @@ -89,17 +103,17 @@ module ActiveSupport #:nodoc: new_offset(0) end alias_method :getutc, :utc - + # Returns true if offset == 0 def utc? offset == 0 end - + # Returns the offset value in seconds def utc_offset (offset * 86400).to_i end - + # Layers additional behavior on DateTime#<=> so that Time and ActiveSupport::TimeWithZone instances can be compared with a DateTime def compare_with_coercion(other) other = other.comparable_time if other.respond_to?(:comparable_time) diff --git a/activesupport/lib/active_support/core_ext/enumerable.rb b/activesupport/lib/active_support/core_ext/enumerable.rb index e451e9933a..788f3a7e9e 100644 --- a/activesupport/lib/active_support/core_ext/enumerable.rb +++ b/activesupport/lib/active_support/core_ext/enumerable.rb @@ -64,8 +64,28 @@ module Enumerable end end + # Iterates over a collection, passing the current element *and* the + # +memo+ to the block. Handy for building up hashes or + # reducing collections down to one object. Examples: + # + # %w(foo bar).each_with_object({}) { |str, hsh| hsh[str] = str.upcase } #=> {'foo' => 'FOO', 'bar' => 'BAR'} + # + # *Note* that you can't use immutable objects like numbers, true or false as + # the memo. You would think the following returns 120, but since the memo is + # never changed, it does not. + # + # (1..5).each_with_object(1) { |value, memo| memo *= value } # => 1 + # + def each_with_object(memo, &block) + returning memo do |m| + each do |element| + block.call(element, m) + end + end + end unless [].respond_to?(:each_with_object) + # Convert an enumerable to a hash. Examples: - # + # # people.index_by(&:login) # => { "nextangle" => <Person ...>, "chade-" => <Person ...>, ...} # people.index_by { |person| "#{person.first_name} #{person.last_name}" } diff --git a/activesupport/lib/active_support/core_ext/file/atomic.rb b/activesupport/lib/active_support/core_ext/file/atomic.rb index 4d3cf5423f..f988eff3d9 100644 --- a/activesupport/lib/active_support/core_ext/file/atomic.rb +++ b/activesupport/lib/active_support/core_ext/file/atomic.rb @@ -28,7 +28,7 @@ module ActiveSupport #:nodoc: rescue Errno::ENOENT # No old permissions, write a temp file to determine the defaults check_name = ".permissions_check.#{Thread.current.object_id}.#{Process.pid}.#{rand(1000000)}" - new(check_name, "w") + open(check_name, "w") { } old_stat = stat(check_name) unlink(check_name) end diff --git a/activesupport/lib/active_support/core_ext/hash/conversions.rb b/activesupport/lib/active_support/core_ext/hash/conversions.rb index 2c606b401b..50dc7c61fc 100644 --- a/activesupport/lib/active_support/core_ext/hash/conversions.rb +++ b/activesupport/lib/active_support/core_ext/hash/conversions.rb @@ -156,7 +156,7 @@ module ActiveSupport #:nodoc: XML_FORMATTING[type_name] ? XML_FORMATTING[type_name].call(value) : value, attributes ) - end + end end end diff --git a/activesupport/lib/active_support/core_ext/hash/except.rb b/activesupport/lib/active_support/core_ext/hash/except.rb index f26d01553d..949976d741 100644 --- a/activesupport/lib/active_support/core_ext/hash/except.rb +++ b/activesupport/lib/active_support/core_ext/hash/except.rb @@ -10,7 +10,7 @@ module ActiveSupport #:nodoc: module Except # Returns a new hash without the given keys. def except(*keys) - clone.except!(*keys) + dup.except!(*keys) end # Replaces the hash without the given keys. diff --git a/activesupport/lib/active_support/core_ext/logger.rb b/activesupport/lib/active_support/core_ext/logger.rb index 9c1fd274ac..c622554860 100644 --- a/activesupport/lib/active_support/core_ext/logger.rb +++ b/activesupport/lib/active_support/core_ext/logger.rb @@ -12,5 +12,132 @@ class Logger end_eval end [:debug, :info, :error, :fatal].each {|level| define_around_helper(level) } +end -end
\ No newline at end of file + +require 'logger' + +# Extensions to the built in Ruby logger. +# +# If you want to use the default log formatter as defined in the Ruby core, then you +# will need to set the formatter for the logger as in: +# +# logger.formatter = Formatter.new +# +# You can then specify the datetime format, for example: +# +# logger.datetime_format = "%Y-%m-%d" +# +# Note: This logger is deprecated in favor of ActiveSupport::BufferedLogger +class Logger + # Set to false to disable the silencer + cattr_accessor :silencer + self.silencer = true + + # Silences the logger for the duration of the block. + def silence(temporary_level = Logger::ERROR) + if silencer + begin + old_logger_level, self.level = level, temporary_level + yield self + ensure + self.level = old_logger_level + end + else + yield self + end + end + + alias :old_datetime_format= :datetime_format= + # Logging date-time format (string passed to +strftime+). Ignored if the formatter + # does not respond to datetime_format=. + def datetime_format=(datetime_format) + formatter.datetime_format = datetime_format if formatter.respond_to?(:datetime_format=) + end + + alias :old_datetime_format :datetime_format + # Get the logging datetime format. Returns nil if the formatter does not support + # datetime formatting. + def datetime_format + formatter.datetime_format if formatter.respond_to?(:datetime_format) + end + + alias :old_formatter :formatter if method_defined?(:formatter) + # Get the current formatter. The default formatter is a SimpleFormatter which only + # displays the log message + def formatter + @formatter ||= SimpleFormatter.new + end + + unless const_defined? :Formatter + class Formatter + Format = "%s, [%s#%d] %5s -- %s: %s\n" + + attr_accessor :datetime_format + + def initialize + @datetime_format = nil + end + + def call(severity, time, progname, msg) + Format % [severity[0..0], format_datetime(time), $$, severity, progname, + msg2str(msg)] + end + + private + def format_datetime(time) + if @datetime_format.nil? + time.strftime("%Y-%m-%dT%H:%M:%S.") << "%06d " % time.usec + else + time.strftime(@datetime_format) + end + end + + def msg2str(msg) + case msg + when ::String + msg + when ::Exception + "#{ msg.message } (#{ msg.class })\n" << + (msg.backtrace || []).join("\n") + else + msg.inspect + end + end + end + end + + # Simple formatter which only displays the message. + class SimpleFormatter < Logger::Formatter + # This method is invoked when a log event occurs + def call(severity, timestamp, progname, msg) + "#{String === msg ? msg : msg.inspect}\n" + end + end + + private + alias old_format_message format_message + + # Ruby 1.8.3 transposed the msg and progname arguments to format_message. + # We can't test RUBY_VERSION because some distributions don't keep Ruby + # and its standard library in sync, leading to installations of Ruby 1.8.2 + # with Logger from 1.8.3 and vice versa. + if method_defined?(:formatter=) + def format_message(severity, timestamp, progname, msg) + formatter.call(severity, timestamp, progname, msg) + end + else + def format_message(severity, timestamp, msg, progname) + formatter.call(severity, timestamp, progname, msg) + end + + attr_writer :formatter + public :formatter= + + alias old_format_datetime format_datetime + def format_datetime(datetime) datetime end + + alias old_msg2str msg2str + def msg2str(msg) msg end + end +end diff --git a/activesupport/lib/active_support/core_ext/module.rb b/activesupport/lib/active_support/core_ext/module.rb index 34fcbd124b..da8d28ec13 100644 --- a/activesupport/lib/active_support/core_ext/module.rb +++ b/activesupport/lib/active_support/core_ext/module.rb @@ -7,7 +7,17 @@ require 'active_support/core_ext/module/introspection' require 'active_support/core_ext/module/loading' require 'active_support/core_ext/module/aliasing' require 'active_support/core_ext/module/model_naming' +require 'active_support/core_ext/module/synchronization' + +module ActiveSupport + module CoreExtensions + # Various extensions for the Ruby core Module class. + module Module + # Nothing here. Only defined for API documentation purposes. + end + end +end class Module - include ActiveSupport::CoreExt::Module::ModelNaming + include ActiveSupport::CoreExtensions::Module end diff --git a/activesupport/lib/active_support/core_ext/module/aliasing.rb b/activesupport/lib/active_support/core_ext/module/aliasing.rb index 1894e3b0a2..e640f64520 100644 --- a/activesupport/lib/active_support/core_ext/module/aliasing.rb +++ b/activesupport/lib/active_support/core_ext/module/aliasing.rb @@ -1,70 +1,74 @@ -class Module - # Encapsulates the common pattern of: - # - # alias_method :foo_without_feature, :foo - # alias_method :foo, :foo_with_feature - # - # With this, you simply do: - # - # alias_method_chain :foo, :feature - # - # And both aliases are set up for you. - # - # Query and bang methods (foo?, foo!) keep the same punctuation: - # - # alias_method_chain :foo?, :feature - # - # is equivalent to - # - # alias_method :foo_without_feature?, :foo? - # alias_method :foo?, :foo_with_feature? - # - # so you can safely chain foo, foo?, and foo! with the same feature. - def alias_method_chain(target, feature) - # Strip out punctuation on predicates or bang methods since - # e.g. target?_without_feature is not a valid method name. - aliased_target, punctuation = target.to_s.sub(/([?!=])$/, ''), $1 - yield(aliased_target, punctuation) if block_given? - - with_method, without_method = "#{aliased_target}_with_#{feature}#{punctuation}", "#{aliased_target}_without_#{feature}#{punctuation}" - - alias_method without_method, target - alias_method target, with_method - - case - when public_method_defined?(without_method) - public target - when protected_method_defined?(without_method) - protected target - when private_method_defined?(without_method) - private target - end - end +module ActiveSupport + module CoreExtensions + module Module + # Encapsulates the common pattern of: + # + # alias_method :foo_without_feature, :foo + # alias_method :foo, :foo_with_feature + # + # With this, you simply do: + # + # alias_method_chain :foo, :feature + # + # And both aliases are set up for you. + # + # Query and bang methods (foo?, foo!) keep the same punctuation: + # + # alias_method_chain :foo?, :feature + # + # is equivalent to + # + # alias_method :foo_without_feature?, :foo? + # alias_method :foo?, :foo_with_feature? + # + # so you can safely chain foo, foo?, and foo! with the same feature. + def alias_method_chain(target, feature) + # Strip out punctuation on predicates or bang methods since + # e.g. target?_without_feature is not a valid method name. + aliased_target, punctuation = target.to_s.sub(/([?!=])$/, ''), $1 + yield(aliased_target, punctuation) if block_given? + + with_method, without_method = "#{aliased_target}_with_#{feature}#{punctuation}", "#{aliased_target}_without_#{feature}#{punctuation}" - # Allows you to make aliases for attributes, which includes - # getter, setter, and query methods. - # - # Example: - # - # class Content < ActiveRecord::Base - # # has a title attribute - # end - # - # class Email < Content - # alias_attribute :subject, :title - # end - # - # e = Email.find(1) - # e.title # => "Superstars" - # e.subject # => "Superstars" - # e.subject? # => true - # e.subject = "Megastars" - # e.title # => "Megastars" - def alias_attribute(new_name, old_name) - module_eval <<-STR, __FILE__, __LINE__+1 - def #{new_name}; self.#{old_name}; end - def #{new_name}?; self.#{old_name}?; end - def #{new_name}=(v); self.#{old_name} = v; end - STR + alias_method without_method, target + alias_method target, with_method + + case + when public_method_defined?(without_method) + public target + when protected_method_defined?(without_method) + protected target + when private_method_defined?(without_method) + private target + end + end + + # Allows you to make aliases for attributes, which includes + # getter, setter, and query methods. + # + # Example: + # + # class Content < ActiveRecord::Base + # # has a title attribute + # end + # + # class Email < Content + # alias_attribute :subject, :title + # end + # + # e = Email.find(1) + # e.title # => "Superstars" + # e.subject # => "Superstars" + # e.subject? # => true + # e.subject = "Megastars" + # e.title # => "Megastars" + def alias_attribute(new_name, old_name) + module_eval <<-STR, __FILE__, __LINE__+1 + def #{new_name}; self.#{old_name}; end + def #{new_name}?; self.#{old_name}?; end + def #{new_name}=(v); self.#{old_name} = v; end + STR + end + end end end diff --git a/activesupport/lib/active_support/core_ext/module/introspection.rb b/activesupport/lib/active_support/core_ext/module/introspection.rb index 45f3e4bf5c..8beaff4b82 100644 --- a/activesupport/lib/active_support/core_ext/module/introspection.rb +++ b/activesupport/lib/active_support/core_ext/module/introspection.rb @@ -1,86 +1,90 @@ -class Module - # Returns the name of the module containing this one. - # - # p M::N.parent_name # => "M" - def parent_name - unless defined? @parent_name - @parent_name = name =~ /::[^:]+\Z/ ? $`.freeze : nil - end - @parent_name - end +module ActiveSupport + module CoreExtensions + module Module + # Returns the name of the module containing this one. + # + # p M::N.parent_name # => "M" + def parent_name + unless defined? @parent_name + @parent_name = name =~ /::[^:]+\Z/ ? $`.freeze : nil + end + @parent_name + end - # Returns the module which contains this one according to its name. - # - # module M - # module N - # end - # end - # X = M::N - # - # p M::N.parent # => M - # p X.parent # => M - # - # The parent of top-level and anonymous modules is Object. - # - # p M.parent # => Object - # p Module.new.parent # => Object - # - def parent - parent_name ? parent_name.constantize : Object - end + # Returns the module which contains this one according to its name. + # + # module M + # module N + # end + # end + # X = M::N + # + # p M::N.parent # => M + # p X.parent # => M + # + # The parent of top-level and anonymous modules is Object. + # + # p M.parent # => Object + # p Module.new.parent # => Object + # + def parent + parent_name ? parent_name.constantize : Object + end - # Returns all the parents of this module according to its name, ordered from - # nested outwards. The receiver is not contained within the result. - # - # module M - # module N - # end - # end - # X = M::N - # - # p M.parents # => [Object] - # p M::N.parents # => [M, Object] - # p X.parents # => [M, Object] - # - def parents - parents = [] - if parent_name - parts = parent_name.split('::') - until parts.empty? - parents << (parts * '::').constantize - parts.pop + # Returns all the parents of this module according to its name, ordered from + # nested outwards. The receiver is not contained within the result. + # + # module M + # module N + # end + # end + # X = M::N + # + # p M.parents # => [Object] + # p M::N.parents # => [M, Object] + # p X.parents # => [M, Object] + # + def parents + parents = [] + if parent_name + parts = parent_name.split('::') + until parts.empty? + parents << (parts * '::').constantize + parts.pop + end + end + parents << Object unless parents.include? Object + parents end - end - parents << Object unless parents.include? Object - parents - end - if RUBY_VERSION < '1.9' - # Returns the constants that have been defined locally by this object and - # not in an ancestor. This method is exact if running under Ruby 1.9. In - # previous versions it may miss some constants if their definition in some - # ancestor is identical to their definition in the receiver. - def local_constants - inherited = {} + if RUBY_VERSION < '1.9' + # Returns the constants that have been defined locally by this object and + # not in an ancestor. This method is exact if running under Ruby 1.9. In + # previous versions it may miss some constants if their definition in some + # ancestor is identical to their definition in the receiver. + def local_constants + inherited = {} + + ancestors.each do |anc| + next if anc == self + anc.constants.each { |const| inherited[const] = anc.const_get(const) } + end - ancestors.each do |anc| - next if anc == self - anc.constants.each { |const| inherited[const] = anc.const_get(const) } + constants.select do |const| + !inherited.key?(const) || inherited[const].object_id != const_get(const).object_id + end + end + else + def local_constants #:nodoc: + constants(false) + end end - constants.select do |const| - !inherited.key?(const) || inherited[const].object_id != const_get(const).object_id + # Returns the names of the constants defined locally rather than the + # constants themselves. See <tt>local_constants</tt>. + def local_constant_names + local_constants.map { |c| c.to_s } end end - else - def local_constants #:nodoc: - constants(false) - end - end - - # Returns the names of the constants defined locally rather than the - # constants themselves. See <tt>local_constants</tt>. - def local_constant_names - local_constants.map { |c| c.to_s } end end diff --git a/activesupport/lib/active_support/core_ext/module/model_naming.rb b/activesupport/lib/active_support/core_ext/module/model_naming.rb index 5518f5417b..3ec4f3ba11 100644 --- a/activesupport/lib/active_support/core_ext/module/model_naming.rb +++ b/activesupport/lib/active_support/core_ext/module/model_naming.rb @@ -11,12 +11,12 @@ module ActiveSupport end end - module CoreExt + module CoreExtensions module Module - module ModelNaming - def model_name - @model_name ||= ModelName.new(name) - end + # Returns an ActiveSupport::ModelName object for module. It can be + # used to retrieve all kinds of naming-related information. + def model_name + @model_name ||= ModelName.new(name) end end end diff --git a/activesupport/lib/active_support/core_ext/module/synchronization.rb b/activesupport/lib/active_support/core_ext/module/synchronization.rb new file mode 100644 index 0000000000..251606024e --- /dev/null +++ b/activesupport/lib/active_support/core_ext/module/synchronization.rb @@ -0,0 +1,39 @@ +class Module + # Synchronize access around a method, delegating synchronization to a + # particular mutex. A mutex (either a Mutex, or any object that responds to + # #synchronize and yields to a block) must be provided as a final :with option. + # The :with option should be a symbol or string, and can represent a method, + # constant, or instance or class variable. + # Example: + # class SharedCache + # @@lock = Mutex.new + # def expire + # ... + # end + # synchronize :expire, :with => :@@lock + # end + def synchronize(*methods) + options = methods.extract_options! + unless options.is_a?(Hash) && with = options[:with] + raise ArgumentError, "Synchronization needs a mutex. Supply an options hash with a :with key as the last argument (e.g. synchronize :hello, :with => :@mutex)." + end + + methods.each do |method| + aliased_method, punctuation = method.to_s.sub(/([?!=])$/, ''), $1 + + if method_defined?("#{aliased_method}_without_synchronization#{punctuation}") + raise ArgumentError, "#{method} is already synchronized. Double synchronization is not currently supported." + end + + module_eval(<<-EOS, __FILE__, __LINE__) + def #{aliased_method}_with_synchronization#{punctuation}(*args, &block) + #{with}.synchronize do + #{aliased_method}_without_synchronization#{punctuation}(*args, &block) + end + end + EOS + + alias_method_chain method, :synchronization + end + end +end diff --git a/activesupport/lib/active_support/core_ext/object/misc.rb b/activesupport/lib/active_support/core_ext/object/misc.rb index 8384a12327..06a7d05702 100644 --- a/activesupport/lib/active_support/core_ext/object/misc.rb +++ b/activesupport/lib/active_support/core_ext/object/misc.rb @@ -1,9 +1,4 @@ class Object - unless respond_to?(:send!) - # Anticipating Ruby 1.9 neutering send - alias send! send - end - # A Ruby-ized realization of the K combinator, courtesy of Mikael Brockman. # # def foo diff --git a/activesupport/lib/active_support/core_ext/range/blockless_step.rb b/activesupport/lib/active_support/core_ext/range/blockless_step.rb index 39dac85636..6fa1eb5bee 100644 --- a/activesupport/lib/active_support/core_ext/range/blockless_step.rb +++ b/activesupport/lib/active_support/core_ext/range/blockless_step.rb @@ -8,7 +8,7 @@ module ActiveSupport #:nodoc: end if RUBY_VERSION < '1.9' - def step_with_blockless(value, &block) + def step_with_blockless(value = 1, &block) if block_given? step_without_blockless(value, &block) else @@ -18,7 +18,7 @@ module ActiveSupport #:nodoc: end end else - def step_with_blockless(value, &block) + def step_with_blockless(value = 1, &block) if block_given? step_without_blockless(value, &block) else diff --git a/activesupport/lib/active_support/core_ext/rexml.rb b/activesupport/lib/active_support/core_ext/rexml.rb new file mode 100644 index 0000000000..187f3e0f5e --- /dev/null +++ b/activesupport/lib/active_support/core_ext/rexml.rb @@ -0,0 +1,36 @@ +require 'rexml/document' +require 'rexml/entity' + +# Fixes the rexml vulnerability disclosed at: +# http://www.ruby-lang.org/en/news/2008/08/23/dos-vulnerability-in-rexml/ +# This fix is identical to rexml-expansion-fix version 1.0.1 + +# Earlier versions of rexml defined REXML::Version, newer ones REXML::VERSION +unless (defined?(REXML::VERSION) ? REXML::VERSION : REXML::Version) > "3.1.7.2" + module REXML + class Entity < Child + undef_method :unnormalized + def unnormalized + document.record_entity_expansion! if document + v = value() + return nil if v.nil? + @unnormalized = Text::unnormalize(v, parent) + @unnormalized + end + end + class Document < Element + @@entity_expansion_limit = 10_000 + def self.entity_expansion_limit= val + @@entity_expansion_limit = val + end + + def record_entity_expansion! + @number_of_expansions ||= 0 + @number_of_expansions += 1 + if @number_of_expansions > @@entity_expansion_limit + raise "Number of entity expansions exceeded, processing aborted." + end + end + end + end +end diff --git a/activesupport/lib/active_support/core_ext/string.rb b/activesupport/lib/active_support/core_ext/string.rb index 25386af70a..7ff2f11eff 100644 --- a/activesupport/lib/active_support/core_ext/string.rb +++ b/activesupport/lib/active_support/core_ext/string.rb @@ -6,6 +6,7 @@ require 'active_support/core_ext/string/iterators' require 'active_support/core_ext/string/unicode' require 'active_support/core_ext/string/xchar' require 'active_support/core_ext/string/filters' +require 'active_support/core_ext/string/behavior' class String #:nodoc: include ActiveSupport::CoreExtensions::String::Access @@ -15,4 +16,5 @@ class String #:nodoc: include ActiveSupport::CoreExtensions::String::StartsEndsWith include ActiveSupport::CoreExtensions::String::Iterators include ActiveSupport::CoreExtensions::String::Unicode + include ActiveSupport::CoreExtensions::String::Behavior end diff --git a/activesupport/lib/active_support/core_ext/string/behavior.rb b/activesupport/lib/active_support/core_ext/string/behavior.rb new file mode 100644 index 0000000000..a93ca3027f --- /dev/null +++ b/activesupport/lib/active_support/core_ext/string/behavior.rb @@ -0,0 +1,13 @@ +module ActiveSupport #:nodoc: + module CoreExtensions #:nodoc: + module String #:nodoc: + module Behavior + # Enable more predictable duck-typing on String-like classes. See + # Object#acts_like?. + def acts_like_string? + true + end + end + end + end +end
\ No newline at end of file diff --git a/activesupport/lib/active_support/core_ext/string/inflections.rb b/activesupport/lib/active_support/core_ext/string/inflections.rb index 3bbad7dad8..de99fe5791 100644 --- a/activesupport/lib/active_support/core_ext/string/inflections.rb +++ b/activesupport/lib/active_support/core_ext/string/inflections.rb @@ -87,6 +87,25 @@ module ActiveSupport #:nodoc: Inflector.demodulize(self) end + # Replaces special characters in a string so that it may be used as part of a 'pretty' URL. + # + # ==== Examples + # + # class Person + # def to_param + # "#{id}-#{name.parameterize}" + # end + # end + # + # @person = Person.find(1) + # # => #<Person id: 1, name: "Donald E. Knuth"> + # + # <%= link_to(@person.name, person_path %> + # # => <a href="/person/1-donald-e-knuth">Donald E. Knuth</a> + def parameterize + Inflector.parameterize(self) + end + # Creates the name of a table like Rails does for models to table names. This method # uses the +pluralize+ method on the last word in the string. # diff --git a/activesupport/lib/active_support/core_ext/time.rb b/activesupport/lib/active_support/core_ext/time.rb index ea50511a96..78bbfc917c 100644 --- a/activesupport/lib/active_support/core_ext/time.rb +++ b/activesupport/lib/active_support/core_ext/time.rb @@ -1,11 +1,32 @@ require 'date' require 'time' -# Ruby 1.8-cvs and 1.9 define private Time#to_date class Time + # Ruby 1.8-cvs and 1.9 define private Time#to_date %w(to_date to_datetime).each do |method| public method if private_instance_methods.include?(method) end + + # Pre-1.9 versions of Ruby have a bug with marshaling Time instances, where utc instances are + # unmarshaled in the local zone, instead of utc. We're layering behavior on the _dump and _load + # methods so that utc instances can be flagged on dump, and coerced back to utc on load. + if RUBY_VERSION < '1.9' + class << self + alias_method :_original_load, :_load + def _load(marshaled_time) + time = _original_load(marshaled_time) + utc = time.instance_variable_get('@marshal_with_utc_coercion') + utc ? time.utc : time + end + end + + alias_method :_original_dump, :_dump + def _dump(*args) + obj = self.frozen? ? self.dup : self + obj.instance_variable_set('@marshal_with_utc_coercion', utc?) + obj._original_dump(*args) + end + end end require 'active_support/core_ext/time/behavior' diff --git a/activesupport/lib/active_support/core_ext/time/calculations.rb b/activesupport/lib/active_support/core_ext/time/calculations.rb index cd234c9b89..3cc6d59907 100644 --- a/activesupport/lib/active_support/core_ext/time/calculations.rb +++ b/activesupport/lib/active_support/core_ext/time/calculations.rb @@ -9,13 +9,13 @@ module ActiveSupport #:nodoc: base.class_eval do alias_method :plus_without_duration, :+ alias_method :+, :plus_with_duration - + alias_method :minus_without_duration, :- alias_method :-, :minus_with_duration - + alias_method :minus_without_coercion, :- alias_method :-, :minus_with_coercion - + alias_method :compare_without_coercion, :<=> alias_method :<=>, :compare_with_coercion end @@ -28,9 +28,9 @@ module ActiveSupport #:nodoc: def ===(other) other.is_a?(::Time) end - - # Return the number of days in the given month. - # If no year is specified, it will use the current year. + + # Return the number of days in the given month. + # If no year is specified, it will use the current year. def days_in_month(month, year = now.year) return 29 if month == 2 && ::Date.gregorian_leap?(year) COMMON_YEAR_DAYS_IN_MONTH[month] @@ -57,6 +57,21 @@ module ActiveSupport #:nodoc: 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? + self.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 self.to_i - self.change(:hour => 0).to_i + (self.usec/1.0e+6) @@ -106,7 +121,7 @@ module ActiveSupport #:nodoc: (seconds.abs >= 86400 && initial_dst != final_dst) ? f + (initial_dst - final_dst).hours : f end rescue - self.to_datetime.since(seconds) + self.to_datetime.since(seconds) end alias :in :since @@ -199,7 +214,7 @@ module ActiveSupport #:nodoc: change(:day => last_day, :hour => 23, :min => 59, :sec => 59, :usec => 0) end alias :at_end_of_month :end_of_month - + # Returns a new Time representing the start of the quarter (1st of january, april, july, october, 0:00) def beginning_of_quarter beginning_of_month.change(:month => [10, 7, 4, 1].detect { |m| m <= self.month }) @@ -208,7 +223,7 @@ module ActiveSupport #:nodoc: # Returns a new Time representing the end of the quarter (last day of march, june, september, december, 23:59:59) def end_of_quarter - change(:month => [3, 6, 9, 12].detect { |m| m >= self.month }).end_of_month + beginning_of_month.change(:month => [3, 6, 9, 12].detect { |m| m >= self.month }).end_of_month end alias :at_end_of_quarter :end_of_quarter @@ -249,7 +264,7 @@ module ActiveSupport #:nodoc: minus_without_duration(other) end end - + # Time#- can also be used to determine the number of seconds between two Time instances. # We're layering on additional behavior so that ActiveSupport::TimeWithZone instances # are coerced into values that Time#- will recognize @@ -257,7 +272,7 @@ module ActiveSupport #:nodoc: other = other.comparable_time if other.respond_to?(:comparable_time) minus_without_coercion(other) end - + # Layers additional behavior on Time#<=> so that DateTime and ActiveSupport::TimeWithZone instances # can be chronologically compared with a Time def compare_with_coercion(other) diff --git a/activesupport/lib/active_support/core_ext/time/zones.rb b/activesupport/lib/active_support/core_ext/time/zones.rb index 079ecdd48e..9d8eb73908 100644 --- a/activesupport/lib/active_support/core_ext/time/zones.rb +++ b/activesupport/lib/active_support/core_ext/time/zones.rb @@ -78,7 +78,7 @@ module ActiveSupport #:nodoc: # # Time.utc(2000).in_time_zone('Alaska') # => Fri, 31 Dec 1999 15:00:00 AKST -09:00 def in_time_zone(zone = ::Time.zone) - ActiveSupport::TimeWithZone.new(utc? ? self : getutc, ::Time.send!(:get_zone, zone)) + ActiveSupport::TimeWithZone.new(utc? ? self : getutc, ::Time.__send__(:get_zone, zone)) end end end diff --git a/activesupport/lib/active_support/deprecation.rb b/activesupport/lib/active_support/deprecation.rb index 01eb5df593..950bca60a6 100644 --- a/activesupport/lib/active_support/deprecation.rb +++ b/activesupport/lib/active_support/deprecation.rb @@ -109,7 +109,7 @@ module ActiveSupport end def deprecation_horizon - '2.0' + '2.3' end end @@ -162,6 +162,22 @@ module ActiveSupport end end + class DeprecatedObjectProxy < DeprecationProxy + def initialize(object, message) + @object = object + @message = message + end + + private + def target + @object + end + + def warn(callstack, called, args) + ActiveSupport::Deprecation.warn(@message, callstack) + end + end + # Stand-in for <tt>@request</tt>, <tt>@attributes</tt>, <tt>@params</tt>, etc. # which emits deprecation warnings on any method call (except +inspect+). class DeprecatedInstanceVariableProxy < DeprecationProxy #:nodoc: diff --git a/activesupport/lib/active_support/inflector.rb b/activesupport/lib/active_support/inflector.rb index 7ae9e0c6ab..8a917a9eb2 100644 --- a/activesupport/lib/active_support/inflector.rb +++ b/activesupport/lib/active_support/inflector.rb @@ -240,6 +240,25 @@ module ActiveSupport def demodulize(class_name_in_module) class_name_in_module.to_s.gsub(/^.*::/, '') end + + # Replaces special characters in a string so that it may be used as part of a 'pretty' URL. + # + # ==== Examples + # + # class Person + # def to_param + # "#{id}-#{name.parameterize}" + # end + # end + # + # @person = Person.find(1) + # # => #<Person id: 1, name: "Donald E. Knuth"> + # + # <%= link_to(@person.name, person_path %> + # # => <a href="/person/1-donald-e-knuth">Donald E. Knuth</a> + def parameterize(string, sep = '-') + string.chars.normalize(:kd).to_s.gsub(/[^\x00-\x7F]+/, '').gsub(/[^a-z0-9_\-]+/i, sep).downcase + end # Create the name of a table like Rails does for models to table names. This method # uses the +pluralize+ method on the last word in the string. diff --git a/activesupport/lib/active_support/memoizable.rb b/activesupport/lib/active_support/memoizable.rb index 6506238ac0..4786fd6e0b 100644 --- a/activesupport/lib/active_support/memoizable.rb +++ b/activesupport/lib/active_support/memoizable.rb @@ -60,7 +60,7 @@ module ActiveSupport #{memoized_ivar} ||= {} unless frozen? reload = args.pop if args.last == true || args.last == :reload - if #{memoized_ivar} + if defined?(#{memoized_ivar}) && #{memoized_ivar} if !reload && #{memoized_ivar}.has_key?(args) #{memoized_ivar}[args] elsif #{memoized_ivar} diff --git a/activesupport/lib/active_support/multibyte/chars.rb b/activesupport/lib/active_support/multibyte/chars.rb index 185d03020c..de2c83f8d1 100644 --- a/activesupport/lib/active_support/multibyte/chars.rb +++ b/activesupport/lib/active_support/multibyte/chars.rb @@ -49,6 +49,11 @@ module ActiveSupport::Multibyte #:nodoc: false end + # Enable more predictable duck-typing on String-like classes. See Object#acts_like?. + def acts_like_string? + true + end + # Create a new Chars instance. def initialize(str) @string = str.respond_to?(:string) ? str.string : str diff --git a/activesupport/lib/active_support/option_merger.rb b/activesupport/lib/active_support/option_merger.rb index c77bca1ac9..b563b093ed 100644 --- a/activesupport/lib/active_support/option_merger.rb +++ b/activesupport/lib/active_support/option_merger.rb @@ -11,7 +11,7 @@ module ActiveSupport private def method_missing(method, *arguments, &block) arguments << (arguments.last.respond_to?(:to_hash) ? @options.deep_merge(arguments.pop) : @options.dup) - @context.send!(method, *arguments, &block) + @context.__send__(method, *arguments, &block) end end end diff --git a/activesupport/lib/active_support/secure_random.rb b/activesupport/lib/active_support/secure_random.rb new file mode 100644 index 0000000000..688165f9a3 --- /dev/null +++ b/activesupport/lib/active_support/secure_random.rb @@ -0,0 +1,197 @@ +begin + require 'openssl' +rescue LoadError +end + +begin + require 'securerandom' +rescue LoadError +end + +module ActiveSupport + if defined?(::SecureRandom) + # Use Ruby 1.9's SecureRandom library whenever possible. + SecureRandom = ::SecureRandom # :nodoc: + else + # = Secure random number generator interface. + # + # This library is an interface for secure random number generator which is + # suitable for generating session key in HTTP cookies, etc. + # + # It supports following secure random number generators. + # + # * openssl + # * /dev/urandom + # * Win32 + # + # *Note*: This module is based on the SecureRandom library from Ruby 1.9, + # revision 18786, August 23 2008. It's 100% interface-compatible with Ruby 1.9's + # SecureRandom library. + # + # == Example + # + # # random hexadecimal string. + # p SecureRandom.hex(10) #=> "52750b30ffbc7de3b362" + # p SecureRandom.hex(10) #=> "92b15d6c8dc4beb5f559" + # p SecureRandom.hex(11) #=> "6aca1b5c58e4863e6b81b8" + # p SecureRandom.hex(12) #=> "94b2fff3e7fd9b9c391a2306" + # p SecureRandom.hex(13) #=> "39b290146bea6ce975c37cfc23" + # ... + # + # # random base64 string. + # p SecureRandom.base64(10) #=> "EcmTPZwWRAozdA==" + # p SecureRandom.base64(10) #=> "9b0nsevdwNuM/w==" + # p SecureRandom.base64(10) #=> "KO1nIU+p9DKxGg==" + # p SecureRandom.base64(11) #=> "l7XEiFja+8EKEtY=" + # p SecureRandom.base64(12) #=> "7kJSM/MzBJI+75j8" + # p SecureRandom.base64(13) #=> "vKLJ0tXBHqQOuIcSIg==" + # ... + # + # # random binary string. + # p SecureRandom.random_bytes(10) #=> "\016\t{\370g\310pbr\301" + # p SecureRandom.random_bytes(10) #=> "\323U\030TO\234\357\020\a\337" + # ... + module SecureRandom + # SecureRandom.random_bytes generates a random binary string. + # + # The argument n specifies the length of the result string. + # + # If n is not specified, 16 is assumed. + # It may be larger in future. + # + # If secure random number generator is not available, + # NotImplementedError is raised. + def self.random_bytes(n=nil) + n ||= 16 + + if defined? OpenSSL::Random + return OpenSSL::Random.random_bytes(n) + end + + if !defined?(@has_urandom) || @has_urandom + flags = File::RDONLY + flags |= File::NONBLOCK if defined? File::NONBLOCK + flags |= File::NOCTTY if defined? File::NOCTTY + flags |= File::NOFOLLOW if defined? File::NOFOLLOW + begin + File.open("/dev/urandom", flags) {|f| + unless f.stat.chardev? + raise Errno::ENOENT + end + @has_urandom = true + ret = f.readpartial(n) + if ret.length != n + raise NotImplementedError, "Unexpected partial read from random device" + end + return ret + } + rescue Errno::ENOENT + @has_urandom = false + end + end + + if !defined?(@has_win32) + begin + require 'Win32API' + + crypt_acquire_context = Win32API.new("advapi32", "CryptAcquireContext", 'PPPII', 'L') + @crypt_gen_random = Win32API.new("advapi32", "CryptGenRandom", 'LIP', 'L') + + hProvStr = " " * 4 + prov_rsa_full = 1 + crypt_verifycontext = 0xF0000000 + + if crypt_acquire_context.call(hProvStr, nil, nil, prov_rsa_full, crypt_verifycontext) == 0 + raise SystemCallError, "CryptAcquireContext failed: #{lastWin32ErrorMessage}" + end + @hProv, = hProvStr.unpack('L') + + @has_win32 = true + rescue LoadError + @has_win32 = false + end + end + if @has_win32 + bytes = " " * n + if @crypt_gen_random.call(@hProv, bytes.size, bytes) == 0 + raise SystemCallError, "CryptGenRandom failed: #{lastWin32ErrorMessage}" + end + return bytes + end + + raise NotImplementedError, "No random device" + end + + # SecureRandom.hex generates a random hex string. + # + # The argument n specifies the length of the random length. + # The length of the result string is twice of n. + # + # If n is not specified, 16 is assumed. + # It may be larger in future. + # + # If secure random number generator is not available, + # NotImplementedError is raised. + def self.hex(n=nil) + random_bytes(n).unpack("H*")[0] + end + + # SecureRandom.base64 generates a random base64 string. + # + # The argument n specifies the length of the random length. + # The length of the result string is about 4/3 of n. + # + # If n is not specified, 16 is assumed. + # It may be larger in future. + # + # If secure random number generator is not available, + # NotImplementedError is raised. + def self.base64(n=nil) + [random_bytes(n)].pack("m*").delete("\n") + end + + # SecureRandom.random_number generates a random number. + # + # If an positive integer is given as n, + # SecureRandom.random_number returns an integer: + # 0 <= SecureRandom.random_number(n) < n. + # + # If 0 is given or an argument is not given, + # SecureRandom.random_number returns an float: + # 0.0 <= SecureRandom.random_number() < 1.0. + def self.random_number(n=0) + if 0 < n + hex = n.to_s(16) + hex = '0' + hex if (hex.length & 1) == 1 + bin = [hex].pack("H*") + mask = bin[0].ord + mask |= mask >> 1 + mask |= mask >> 2 + mask |= mask >> 4 + begin + rnd = SecureRandom.random_bytes(bin.length) + rnd[0] = (rnd[0].ord & mask).chr + end until rnd < bin + rnd.unpack("H*")[0].hex + else + # assumption: Float::MANT_DIG <= 64 + i64 = SecureRandom.random_bytes(8).unpack("Q")[0] + Math.ldexp(i64 >> (64-Float::MANT_DIG), -Float::MANT_DIG) + end + end + + # Following code is based on David Garamond's GUID library for Ruby. + def self.lastWin32ErrorMessage # :nodoc: + get_last_error = Win32API.new("kernel32", "GetLastError", '', 'L') + format_message = Win32API.new("kernel32", "FormatMessageA", 'LPLLPLPPPPPPPP', 'L') + format_message_ignore_inserts = 0x00000200 + format_message_from_system = 0x00001000 + + code = get_last_error.call + msg = "\0" * 1024 + len = format_message.call(format_message_ignore_inserts + format_message_from_system, 0, code, 0, msg, 1024, nil, nil, nil, nil, nil, nil, nil, nil) + msg[0, len].tr("\r", '').chomp + end + end + end +end diff --git a/activesupport/lib/active_support/string_inquirer.rb b/activesupport/lib/active_support/string_inquirer.rb index 65545748df..cd722a3cfb 100644 --- a/activesupport/lib/active_support/string_inquirer.rb +++ b/activesupport/lib/active_support/string_inquirer.rb @@ -1,4 +1,14 @@ module ActiveSupport + # Wrapping a string in this class gives you a prettier way to test + # for equality. The value returned by <tt>Rails.env</tt> is wrapped + # in a StringInquirer object so instead of calling this: + # + # Rails.env == "production" + # + # you can call this: + # + # Rails.env.production? + # class StringInquirer < String def method_missing(method_name, *arguments) if method_name.to_s.ends_with?("?") diff --git a/activesupport/lib/active_support/test_case.rb b/activesupport/lib/active_support/test_case.rb index 0f531b0c79..197e73b3e8 100644 --- a/activesupport/lib/active_support/test_case.rb +++ b/activesupport/lib/active_support/test_case.rb @@ -12,7 +12,13 @@ module ActiveSupport 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 - define_method(test_name, &block) + 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 diff --git a/activesupport/lib/active_support/testing/core_ext/test/unit/assertions.rb b/activesupport/lib/active_support/testing/core_ext/test/unit/assertions.rb index 70a44eab8c..63d1ba6507 100644 --- a/activesupport/lib/active_support/testing/core_ext/test/unit/assertions.rb +++ b/activesupport/lib/active_support/testing/core_ext/test/unit/assertions.rb @@ -36,7 +36,11 @@ module Test # post :delete, :id => ... # end def assert_difference(expressions, difference = 1, message = nil, &block) - expression_evaluations = Array(expressions).collect{ |expression| lambda { eval(expression, block.send!(:binding)) } } + expression_evaluations = Array(expressions).map do |expression| + lambda do + eval(expression, block.__send__(:binding)) + end + end original_values = expression_evaluations.inject([]) { |memo, expression| memo << expression.call } yield diff --git a/activesupport/lib/active_support/time_with_zone.rb b/activesupport/lib/active_support/time_with_zone.rb index 4866fa0dc8..b7b8807c6d 100644 --- a/activesupport/lib/active_support/time_with_zone.rb +++ b/activesupport/lib/active_support/time_with_zone.rb @@ -1,6 +1,6 @@ require 'tzinfo' module ActiveSupport - # A Time-like class that can represent a time in any time zone. Necessary because standard Ruby Time instances are + # A Time-like class that can represent a time in any time zone. Necessary because standard Ruby Time instances are # limited to UTC and the system's <tt>ENV['TZ']</tt> zone. # # You shouldn't ever need to create a TimeWithZone instance directly via <tt>new</tt> -- instead, Rails provides the methods @@ -32,12 +32,12 @@ module ActiveSupport class TimeWithZone include Comparable attr_reader :time_zone - + def initialize(utc_time, time_zone, local_time = nil, period = nil) @utc, @time_zone, @time = utc_time, time_zone, local_time @period = @utc ? period : get_period_and_ensure_valid_local_time end - + # Returns a Time or DateTime instance that represents the time in +time_zone+. def time @time ||= period.to_local(@utc) @@ -51,7 +51,7 @@ module ActiveSupport alias_method :getgm, :utc alias_method :getutc, :utc alias_method :gmtime, :utc - + # Returns the underlying TZInfo::TimezonePeriod. def period @period ||= time_zone.period_for_utc(@utc) @@ -62,38 +62,38 @@ module ActiveSupport return self if time_zone == new_zone utc.in_time_zone(new_zone) end - + # Returns a <tt>Time.local()</tt> instance of the simultaneous time in your system's <tt>ENV['TZ']</tt> zone def localtime utc.getlocal end alias_method :getlocal, :localtime - + def dst? period.dst? end alias_method :isdst, :dst? - + def utc? time_zone.name == 'UTC' end alias_method :gmt?, :utc? - + def utc_offset period.utc_total_offset end alias_method :gmt_offset, :utc_offset alias_method :gmtoff, :utc_offset - + def formatted_offset(colon = true, alternate_utc_string = nil) utc? && alternate_utc_string || utc_offset.to_utc_offset_s(colon) end - + # Time uses +zone+ to display the time zone abbreviation, so we're duck-typing it. def zone period.zone_identifier.to_s end - + def inspect "#{time.strftime('%a, %d %b %Y %H:%M:%S')} #{zone} #{formatted_offset}" end @@ -122,7 +122,7 @@ module ActiveSupport %("#{time.strftime("%Y/%m/%d %H:%M:%S")} #{formatted_offset(false)}") end end - + def to_yaml(options = {}) if options.kind_of?(YAML::Emitter) utc.to_yaml(options) @@ -130,19 +130,19 @@ module ActiveSupport time.to_yaml(options).gsub('Z', formatted_offset(true, 'Z')) end end - + def httpdate utc.httpdate end - + def rfc2822 to_s(:rfc822) end alias_method :rfc822, :rfc2822 - + # <tt>:db</tt> format outputs time in UTC; all others output time in local. # Uses TimeWithZone's +strftime+, so <tt>%Z</tt> and <tt>%z</tt> work correctly. - def to_s(format = :default) + def to_s(format = :default) return utc.to_s(format) if format == :db if formatter = ::Time::DATE_FORMATS[format] formatter.respond_to?(:call) ? formatter.call(self).to_s : strftime(formatter) @@ -150,27 +150,39 @@ module ActiveSupport "#{time.strftime("%Y-%m-%d %H:%M:%S")} #{formatted_offset(false, 'UTC')}" # mimicking Ruby 1.9 Time#to_s format end end - + # Replaces <tt>%Z</tt> and <tt>%z</tt> directives with +zone+ and +formatted_offset+, respectively, before passing to # Time#strftime, so that zone information is correct def strftime(format) format = format.gsub('%Z', zone).gsub('%z', formatted_offset(false)) time.strftime(format) end - + # Use the time in UTC for comparisons. def <=>(other) utc <=> other end - + def between?(min, max) utc.between?(min, max) end - + + def past? + utc.past? + end + + def today? + time.today? + end + + def future? + utc.future? + end + def eql?(other) utc == other end - + def +(other) # If we're adding a Duration of variable length (i.e., years, months, days), move forward from #time, # otherwise move forward from #utc, for accuracy when moving across DST boundaries @@ -194,7 +206,7 @@ module ActiveSupport result.in_time_zone(time_zone) end end - + def since(other) # If we're adding a Duration of variable length (i.e., years, months, days), move forward from #time, # otherwise move forward from #utc, for accuracy when moving across DST boundaries @@ -204,7 +216,7 @@ module ActiveSupport utc.since(other).in_time_zone(time_zone) end end - + def ago(other) since(-other) end @@ -218,53 +230,53 @@ module ActiveSupport utc.advance(options).in_time_zone(time_zone) end end - - %w(year mon month day mday hour min sec).each do |method_name| + + %w(year mon month day mday wday yday hour min sec to_date).each do |method_name| class_eval <<-EOV def #{method_name} time.#{method_name} end EOV end - + def usec time.respond_to?(:usec) ? time.usec : 0 end - + def to_a [time.sec, time.min, time.hour, time.day, time.mon, time.year, time.wday, time.yday, dst?, zone] end - + def to_f utc.to_f - end - + end + def to_i utc.to_i end alias_method :hash, :to_i alias_method :tv_sec, :to_i - + # A TimeWithZone acts like a Time, so just return +self+. def to_time self end - + def to_datetime utc.to_datetime.new_offset(Rational(utc_offset, 86_400)) end - + # So that +self+ <tt>acts_like?(:time)</tt>. def acts_like_time? true end - + # Say we're a Time to thwart type checking. def is_a?(klass) klass == ::Time || super end alias_method :kind_of?, :is_a? - + # Neuter freeze because freezing can cause problems with lazy loading of attributes. def freeze self @@ -273,9 +285,9 @@ module ActiveSupport def marshal_dump [utc, time_zone.name, time] end - + def marshal_load(variables) - initialize(variables[0].utc, ::Time.send!(:get_zone, variables[1]), variables[2].utc) + initialize(variables[0].utc, ::Time.__send__(:get_zone, variables[1]), variables[2].utc) end # Ensure proxy class responds to all methods that underlying time instance responds to. @@ -290,10 +302,10 @@ module ActiveSupport result = time.__send__(sym, *args, &block) result.acts_like?(:time) ? self.class.new(nil, time_zone, result) : result end - - private + + private def get_period_and_ensure_valid_local_time - # we don't want a Time.local instance enforcing its own DST rules as well, + # we don't want a Time.local instance enforcing its own DST rules as well, # so transfer time values to a utc constructor if necessary @time = transfer_time_values_to_utc_constructor(@time) unless @time.utc? begin @@ -304,11 +316,11 @@ module ActiveSupport retry end end - + def transfer_time_values_to_utc_constructor(time) ::Time.utc_time(time.year, time.month, time.day, time.hour, time.min, time.sec, time.respond_to?(:usec) ? time.usec : 0) end - + def duration_of_variable_length?(obj) ActiveSupport::Duration === obj && obj.parts.flatten.detect {|p| [:years, :months, :days].include? p } end diff --git a/activesupport/lib/active_support/values/time_zone.rb b/activesupport/lib/active_support/values/time_zone.rb index 788d40bfa8..a294dd0ba3 100644 --- a/activesupport/lib/active_support/values/time_zone.rb +++ b/activesupport/lib/active_support/values/time_zone.rb @@ -124,7 +124,7 @@ module ActiveSupport "Kathmandu" => "Asia/Katmandu", "Astana" => "Asia/Dhaka", "Dhaka" => "Asia/Dhaka", - "Sri Jayawardenepura" => "Asia/Dhaka", + "Sri Jayawardenepura" => "Asia/Colombo", "Almaty" => "Asia/Almaty", "Novosibirsk" => "Asia/Novosibirsk", "Rangoon" => "Asia/Rangoon", diff --git a/activesupport/lib/active_support/vendor/i18n-0.0.1/i18n.rb b/activesupport/lib/active_support/vendor/i18n-0.0.1/i18n.rb index 9e347d94e9..344c77aecf 100755 --- a/activesupport/lib/active_support/vendor/i18n-0.0.1/i18n.rb +++ b/activesupport/lib/active_support/vendor/i18n-0.0.1/i18n.rb @@ -10,7 +10,8 @@ require 'i18n/exceptions' module I18n @@backend = nil - @@default_locale = 'en-US' + @@load_path = nil + @@default_locale = :'en-US' @@exception_handler = :default_exception_handler class << self @@ -49,26 +50,22 @@ module I18n @@exception_handler = exception_handler end - # Allow client libraries to pass a block that populates the translation - # storage. Decoupled for backends like a db backend that persist their - # translations, so the backend can decide whether/when to yield or not. - def populate(&block) - backend.populate(&block) - end - - # Allows client libraries to pass arguments that specify a source for - # translation data to be loaded by the backend. The backend defines - # acceptable sources. + # Allow clients to register paths providing translation data sources. The + # backend defines acceptable sources. + # # E.g. the provided SimpleBackend accepts a list of paths to translation # files which are either named *.rb and contain plain Ruby Hashes or are - # named *.yml and contain YAML data.) - def load_translations(*args) - backend.load_translations(*args) + # named *.yml and contain YAML data. So for the SimpleBackend clients may + # register translation files like this: + # I18n.load_path << 'path/to/locale/en-US.yml' + def load_path + @@load_path ||= [] end - - # Stores translations for the given locale in the backend. - def store_translations(locale, data) - backend.store_translations locale, data + + # Sets the load path instance. Custom implementations are expected to + # behave like a Ruby Array. + def load_path=(load_path) + @@load_path = load_path end # Translates, pluralizes and interpolates a given key using a given locale, @@ -187,6 +184,4 @@ module I18n keys.flatten.map{|k| k.to_sym} end end -end - - +end
\ No newline at end of file diff --git a/activesupport/lib/active_support/vendor/i18n-0.0.1/i18n/backend/simple.rb b/activesupport/lib/active_support/vendor/i18n-0.0.1/i18n/backend/simple.rb index a53f7fe772..2dbaf8a405 100644 --- a/activesupport/lib/active_support/vendor/i18n-0.0.1/i18n/backend/simple.rb +++ b/activesupport/lib/active_support/vendor/i18n-0.0.1/i18n/backend/simple.rb @@ -1,15 +1,11 @@ -require 'strscan' +require 'yaml' module I18n module Backend class Simple - # Allow client libraries to pass a block that populates the translation - # storage. Decoupled for backends like a db backend that persist their - # translations, so the backend can decide whether/when to yield or not. - def populate(&block) - yield - end - + INTERPOLATION_RESERVED_KEYS = %w(scope default) + MATCH = /(\\\\)?\{\{([^\}]+)\}\}/ + # Accepts a list of paths to translation files. Loads translations from # plain Ruby (*.rb) or YAML files (*.yml). See #load_rb and #load_yml # for details. @@ -47,12 +43,15 @@ module I18n raise ArgumentError, "Object must be a Date, DateTime or Time object. #{object.inspect} given." unless object.respond_to?(:strftime) type = object.respond_to?(:sec) ? 'time' : 'date' + # TODO only translate these if format is a String? formats = translate(locale, :"#{type}.formats") format = formats[format.to_sym] if formats && formats[format.to_sym] # TODO raise exception unless format found? format = format.to_s.dup - format.gsub!(/%a/, translate(locale, :"date.abbr_day_names")[object.wday]) + # TODO only translate these if the format string is actually present + # TODO check which format strings are present, then bulk translate then, then replace them + format.gsub!(/%a/, translate(locale, :"date.abbr_day_names")[object.wday]) format.gsub!(/%A/, translate(locale, :"date.day_names")[object.wday]) format.gsub!(/%b/, translate(locale, :"date.abbr_month_names")[object.mon]) format.gsub!(/%B/, translate(locale, :"date.month_names")[object.mon]) @@ -60,7 +59,16 @@ module I18n object.strftime(format) end + def initialized? + @initialized ||= false + end + protected + + def init_translations + load_translations(*I18n.load_path) + @initialized = true + end def translations @translations ||= {} @@ -73,6 +81,7 @@ module I18n # <tt>%w(currency format)</tt>. def lookup(locale, key, scope = []) return unless key + init_translations unless initialized? keys = I18n.send :normalize_translation_keys, locale, key, scope keys.inject(translations){|result, k| result[k.to_sym] or return nil } end @@ -95,7 +104,7 @@ module I18n rescue MissingTranslationData nil end - + # Picks a translation from an array according to English pluralization # rules. It will pick the first translation if count is not equal to 1 # and the second translation if it is equal to 1. Other backends can @@ -108,7 +117,7 @@ module I18n raise InvalidPluralizationData.new(entry, count) unless entry.has_key?(key) entry[key] end - + # Interpolates values into a given string. # # interpolate "file {{file}} opened by \\{{user}}", :file => 'test.txt', :user => 'Mr. X' @@ -118,29 +127,27 @@ module I18n # the <tt>{{...}}</tt> key in a string (once for the string and once for the # interpolation). def interpolate(locale, string, values = {}) - return string if !string.is_a?(String) + return string unless string.is_a?(String) - string = string.gsub(/%d/, '{{count}}').gsub(/%s/, '{{value}}') if string.respond_to?(:force_encoding) - original_encoding = string.encoding - string.force_encoding(Encoding::BINARY) - end - s = StringScanner.new(string) - - while s.skip_until(/\{\{/) - s.string[s.pos - 3, 1] = '' and next if s.pre_match[-1, 1] == '\\' - start_pos = s.pos - 2 - key = s.scan_until(/\}\}/)[0..-3] - end_pos = s.pos - 1 + original_encoding = string.encoding + string.force_encoding(Encoding::BINARY) + end - raise ReservedInterpolationKey.new(key, string) if %w(scope default).include?(key) - raise MissingInterpolationArgument.new(key, string) unless values.has_key? key.to_sym + result = string.gsub(MATCH) do + escaped, pattern, key = $1, $2, $2.to_sym - s.string[start_pos..end_pos] = values[key.to_sym].to_s - s.unscan + if escaped + pattern + elsif INTERPOLATION_RESERVED_KEYS.include?(pattern) + raise ReservedInterpolationKey.new(pattern, string) + elsif !values.include?(key) + raise MissingInterpolationArgument.new(pattern, string) + else + values[key].to_s + end end - - result = s.string + result.force_encoding(original_encoding) if original_encoding result end diff --git a/activesupport/lib/active_support/vendor/tzinfo-0.3.9/tzinfo/definitions/Asia/Colombo.rb b/activesupport/lib/active_support/vendor/tzinfo-0.3.9/tzinfo/definitions/Asia/Colombo.rb new file mode 100644 index 0000000000..f6531fa819 --- /dev/null +++ b/activesupport/lib/active_support/vendor/tzinfo-0.3.9/tzinfo/definitions/Asia/Colombo.rb @@ -0,0 +1,30 @@ +require 'tzinfo/timezone_definition' + +module TZInfo + module Definitions + module Asia + module Colombo + include TimezoneDefinition + + timezone 'Asia/Colombo' do |tz| + tz.offset :o0, 19164, 0, :LMT + tz.offset :o1, 19172, 0, :MMT + tz.offset :o2, 19800, 0, :IST + tz.offset :o3, 19800, 1800, :IHST + tz.offset :o4, 19800, 3600, :IST + tz.offset :o5, 23400, 0, :LKT + tz.offset :o6, 21600, 0, :LKT + + tz.transition 1879, 12, :o1, 17335550003, 7200 + tz.transition 1905, 12, :o2, 52211763607, 21600 + tz.transition 1942, 1, :o3, 116657485, 48 + tz.transition 1942, 8, :o4, 9722413, 4 + tz.transition 1945, 10, :o2, 38907909, 16 + tz.transition 1996, 5, :o5, 832962600 + tz.transition 1996, 10, :o6, 846266400 + tz.transition 2006, 4, :o2, 1145039400 + end + end + end + end +end diff --git a/activesupport/test/buffered_logger_test.rb b/activesupport/test/buffered_logger_test.rb index 6319c09210..28dd34334f 100644 --- a/activesupport/test/buffered_logger_test.rb +++ b/activesupport/test/buffered_logger_test.rb @@ -134,6 +134,7 @@ class BufferedLoggerTest < Test::Unit::TestCase a.join b.join - assert_equal "a\nb\nc\nx\ny\nz\n", @output.string + assert @output.string.include?("a\nb\nc\n") + assert @output.string.include?("x\ny\nz\n") end end diff --git a/activesupport/test/core_ext/date_ext_test.rb b/activesupport/test/core_ext/date_ext_test.rb index 0f3cf4c75c..b53c754780 100644 --- a/activesupport/test/core_ext/date_ext_test.rb +++ b/activesupport/test/core_ext/date_ext_test.rb @@ -210,6 +210,29 @@ class DateExtCalculationsTest < Test::Unit::TestCase end end + uses_mocha 'past?, today? and future?' do + def test_today + Date.stubs(:current).returns(Date.new(2000, 1, 1)) + assert_equal false, Date.new(1999, 12, 31).today? + assert_equal true, Date.new(2000,1,1).today? + assert_equal false, Date.new(2000,1,2).today? + end + + def test_past + Date.stubs(:current).returns(Date.new(2000, 1, 1)) + assert_equal true, Date.new(1999, 12, 31).past? + assert_equal false, Date.new(2000,1,1).past? + assert_equal false, Date.new(2000,1,2).past? + end + + def test_future + Date.stubs(:current).returns(Date.new(2000, 1, 1)) + assert_equal false, Date.new(1999, 12, 31).future? + assert_equal false, Date.new(2000,1,1).future? + assert_equal true, Date.new(2000,1,2).future? + end + end + uses_mocha 'TestDateCurrent' do def test_current_returns_date_today_when_zone_default_not_set with_env_tz 'US/Central' do diff --git a/activesupport/test/core_ext/date_time_ext_test.rb b/activesupport/test/core_ext/date_time_ext_test.rb index 854a3a05e1..be3cd8b5d6 100644 --- a/activesupport/test/core_ext/date_time_ext_test.rb +++ b/activesupport/test/core_ext/date_time_ext_test.rb @@ -207,6 +207,81 @@ class DateTimeExtCalculationsTest < Test::Unit::TestCase assert_match(/^2080-02-28T15:15:10-06:?00$/, DateTime.civil(2080, 2, 28, 15, 15, 10, -0.25).xmlschema) end + uses_mocha 'Test DateTime past?, today? and future?' do + def test_today_with_offset + Date.stubs(:current).returns(Date.new(2000, 1, 1)) + assert_equal false, DateTime.civil(1999,12,31,23,59,59, Rational(-18000, 86400)).today? + assert_equal true, DateTime.civil(2000,1,1,0,0,0, Rational(-18000, 86400)).today? + assert_equal true, DateTime.civil(2000,1,1,23,59,59, Rational(-18000, 86400)).today? + assert_equal false, DateTime.civil(2000,1,2,0,0,0, Rational(-18000, 86400)).today? + end + + def test_today_without_offset + Date.stubs(:current).returns(Date.new(2000, 1, 1)) + assert_equal false, DateTime.civil(1999,12,31,23,59,59).today? + assert_equal true, DateTime.civil(2000,1,1,0).today? + assert_equal true, DateTime.civil(2000,1,1,23,59,59).today? + assert_equal false, DateTime.civil(2000,1,2,0).today? + end + + def test_past_with_offset + DateTime.stubs(:current).returns(DateTime.civil(2005,2,10,15,30,45, Rational(-18000, 86400))) + assert_equal true, DateTime.civil(2005,2,10,15,30,44, Rational(-18000, 86400)).past? + assert_equal false, DateTime.civil(2005,2,10,15,30,45, Rational(-18000, 86400)).past? + assert_equal false, DateTime.civil(2005,2,10,15,30,46, Rational(-18000, 86400)).past? + end + + def test_past_without_offset + DateTime.stubs(:current).returns(DateTime.civil(2005,2,10,15,30,45, Rational(-18000, 86400))) + assert_equal true, DateTime.civil(2005,2,10,20,30,44).past? + assert_equal false, DateTime.civil(2005,2,10,20,30,45).past? + assert_equal false, DateTime.civil(2005,2,10,20,30,46).past? + end + + def test_future_with_offset + DateTime.stubs(:current).returns(DateTime.civil(2005,2,10,15,30,45, Rational(-18000, 86400))) + assert_equal false, DateTime.civil(2005,2,10,15,30,44, Rational(-18000, 86400)).future? + assert_equal false, DateTime.civil(2005,2,10,15,30,45, Rational(-18000, 86400)).future? + assert_equal true, DateTime.civil(2005,2,10,15,30,46, Rational(-18000, 86400)).future? + end + + def test_future_without_offset + DateTime.stubs(:current).returns(DateTime.civil(2005,2,10,15,30,45, Rational(-18000, 86400))) + assert_equal false, DateTime.civil(2005,2,10,20,30,44).future? + assert_equal false, DateTime.civil(2005,2,10,20,30,45).future? + assert_equal true, DateTime.civil(2005,2,10,20,30,46).future? + end + end + + uses_mocha 'TestDateTimeCurrent' do + def test_current_returns_date_today_when_zone_default_not_set + with_env_tz 'US/Eastern' do + Time.stubs(:now).returns Time.local(1999, 12, 31, 23, 59, 59) + assert_equal DateTime.new(1999, 12, 31, 23, 59, 59, Rational(-18000, 86400)), DateTime.current + end + end + + def test_current_returns_time_zone_today_when_zone_default_set + Time.zone_default = ActiveSupport::TimeZone['Eastern Time (US & Canada)'] + with_env_tz 'US/Eastern' do + Time.stubs(:now).returns Time.local(1999, 12, 31, 23, 59, 59) + assert_equal DateTime.new(1999, 12, 31, 23, 59, 59, Rational(-18000, 86400)), DateTime.current + end + ensure + Time.zone_default = nil + end + end + + def test_current_without_time_zone + assert DateTime.current.is_a?(DateTime) + end + + def test_current_with_time_zone + with_env_tz 'US/Eastern' do + assert DateTime.current.is_a?(DateTime) + end + end + def test_acts_like_time assert DateTime.new.acts_like_time? end diff --git a/activesupport/test/core_ext/enumerable_test.rb b/activesupport/test/core_ext/enumerable_test.rb index 2315d8f3db..deb9b7544d 100644 --- a/activesupport/test/core_ext/enumerable_test.rb +++ b/activesupport/test/core_ext/enumerable_test.rb @@ -58,6 +58,11 @@ class EnumerableTests < Test::Unit::TestCase assert_equal Payment.new(0), [].sum(Payment.new(0)) end + def test_each_with_object + result = %w(foo bar).each_with_object({}) { |str, hsh| hsh[str] = str.upcase } + assert_equal({'foo' => 'FOO', 'bar' => 'BAR'}, result) + end + def test_index_by payments = [ Payment.new(5), Payment.new(15), Payment.new(10) ] assert_equal({ 5 => payments[0], 15 => payments[1], 10 => payments[2] }, diff --git a/activesupport/test/core_ext/hash_ext_test.rb b/activesupport/test/core_ext/hash_ext_test.rb index fc8ed45358..44d48e7577 100644 --- a/activesupport/test/core_ext/hash_ext_test.rb +++ b/activesupport/test/core_ext/hash_ext_test.rb @@ -62,7 +62,7 @@ class HashExtTest < Test::Unit::TestCase @symbols = @symbols.with_indifferent_access @mixed = @mixed.with_indifferent_access - assert_equal 'a', @strings.send!(:convert_key, :a) + assert_equal 'a', @strings.__send__(:convert_key, :a) assert_equal 1, @strings.fetch('a') assert_equal 1, @strings.fetch(:a.to_s) @@ -75,9 +75,9 @@ class HashExtTest < Test::Unit::TestCase hashes.each do |name, hash| method_map.sort_by { |m| m.to_s }.each do |meth, expected| - assert_equal(expected, hash.send!(meth, 'a'), + assert_equal(expected, hash.__send__(meth, 'a'), "Calling #{name}.#{meth} 'a'") - assert_equal(expected, hash.send!(meth, :a), + assert_equal(expected, hash.__send__(meth, :a), "Calling #{name}.#{meth} :a") end end @@ -341,6 +341,20 @@ class HashExtTest < Test::Unit::TestCase assert_equal expected, original.except!(:c) assert_equal expected, original end + + def test_except_with_original_frozen + original = { :a => 'x', :b => 'y' } + original.freeze + assert_nothing_raised { original.except(:a) } + end + + uses_mocha 'except with expectation' do + def test_except_with_mocha_expectation_on_original + original = { :a => 'x', :b => 'y' } + original.expects(:delete).never + original.except(:a) + end + end end class IWriteMyOwnXML @@ -733,7 +747,7 @@ class HashToXmlTest < Test::Unit::TestCase def test_empty_string_works_for_typecast_xml_value assert_nothing_raised do - Hash.send!(:typecast_xml_value, "") + Hash.__send__(:typecast_xml_value, "") end end @@ -839,6 +853,27 @@ class QueryTest < Test::Unit::TestCase :person => {:id => [20, 10]} end + def test_expansion_count_is_limited + assert_raises RuntimeError do + attack_xml = <<-EOT + <?xml version="1.0" encoding="UTF-8"?> + <!DOCTYPE member [ + <!ENTITY a "&b;&b;&b;&b;&b;&b;&b;&b;&b;&b;"> + <!ENTITY b "&c;&c;&c;&c;&c;&c;&c;&c;&c;&c;"> + <!ENTITY c "&d;&d;&d;&d;&d;&d;&d;&d;&d;&d;"> + <!ENTITY d "&e;&e;&e;&e;&e;&e;&e;&e;&e;&e;"> + <!ENTITY e "&f;&f;&f;&f;&f;&f;&f;&f;&f;&f;"> + <!ENTITY f "&g;&g;&g;&g;&g;&g;&g;&g;&g;&g;"> + <!ENTITY g "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"> + ]> + <member> + &a; + </member> + EOT + Hash.from_xml(attack_xml) + end + end + private def assert_query_equal(expected, actual, message = nil) assert_equal expected.split('&'), actual.to_query.split('&') diff --git a/activesupport/test/core_ext/module/synchronization_test.rb b/activesupport/test/core_ext/module/synchronization_test.rb new file mode 100644 index 0000000000..b1d4bc5e06 --- /dev/null +++ b/activesupport/test/core_ext/module/synchronization_test.rb @@ -0,0 +1,85 @@ +require 'abstract_unit' + +class SynchronizationTest < Test::Unit::TestCase + def setup + @target = Class.new + @target.cattr_accessor :mutex, :instance_writer => false + @target.mutex = Mutex.new + @instance = @target.new + end + + def test_synchronize_aliases_method_chain_with_synchronize + @target.module_eval do + attr_accessor :value + synchronize :value, :with => :mutex + end + assert @instance.respond_to?(:value_with_synchronization) + assert @instance.respond_to?(:value_without_synchronization) + end + + def test_synchronize_does_not_change_behavior + @target.module_eval do + attr_accessor :value + synchronize :value, :with => :mutex + end + expected = "some state" + @instance.value = expected + assert_equal expected, @instance.value + end + + def test_synchronize_with_no_mutex_raises_an_argument_error + assert_raises(ArgumentError) do + @target.synchronize :to_s + end + end + + def test_double_synchronize_raises_an_argument_error + @target.synchronize :to_s, :with => :mutex + assert_raises(ArgumentError) do + @target.synchronize :to_s, :with => :mutex + end + end + + def dummy_sync + dummy = Object.new + def dummy.synchronize + @sync_count ||= 0 + @sync_count += 1 + yield + end + def dummy.sync_count; @sync_count; end + dummy + end + + def test_mutex_is_entered_during_method_call + @target.mutex = dummy_sync + @target.synchronize :to_s, :with => :mutex + @instance.to_s + @instance.to_s + assert_equal 2, @target.mutex.sync_count + end + + def test_can_synchronize_method_with_punctuation + @target.module_eval do + def dangerous? + @dangerous + end + def dangerous! + @dangerous = true + end + end + @target.synchronize :dangerous?, :dangerous!, :with => :mutex + @instance.dangerous! + assert @instance.dangerous? + end + + def test_can_synchronize_singleton_methods + @target.mutex = dummy_sync + class << @target + synchronize :to_s, :with => :mutex + end + assert @target.respond_to?(:to_s_without_synchronization) + assert_nothing_raised { @target.to_s; @target.to_s } + assert_equal 2, @target.mutex.sync_count + end +end
\ No newline at end of file diff --git a/activesupport/test/core_ext/object_and_class_ext_test.rb b/activesupport/test/core_ext/object_and_class_ext_test.rb index b0a746fdc7..e88dcb52d5 100644 --- a/activesupport/test/core_ext/object_and_class_ext_test.rb +++ b/activesupport/test/core_ext/object_and_class_ext_test.rb @@ -108,11 +108,6 @@ class ClassExtTest < Test::Unit::TestCase end class ObjectTests < Test::Unit::TestCase - def test_send_bang_aliases_send_before_19 - assert_respond_to 'a', :send! - assert_equal 1, 'a'.send!(:size) - end - def test_suppress_re_raises assert_raises(LoadError) { suppress(ArgumentError) {raise LoadError} } end diff --git a/activesupport/test/core_ext/string_ext_test.rb b/activesupport/test/core_ext/string_ext_test.rb index c9f959ef32..c0decf2c3f 100644 --- a/activesupport/test/core_ext/string_ext_test.rb +++ b/activesupport/test/core_ext/string_ext_test.rb @@ -201,3 +201,9 @@ class StringInflectionsTest < Test::Unit::TestCase end end end + +class StringBehaviourTest < Test::Unit::TestCase + def test_acts_like_string + assert 'Bambi'.acts_like_string? + end +end
\ No newline at end of file diff --git a/activesupport/test/core_ext/time_ext_test.rb b/activesupport/test/core_ext/time_ext_test.rb index 8740497b3d..8ceaedc7f4 100644 --- a/activesupport/test/core_ext/time_ext_test.rb +++ b/activesupport/test/core_ext/time_ext_test.rb @@ -117,6 +117,7 @@ class TimeExtCalculationsTest < Test::Unit::TestCase assert_equal Time.local(2007,3,31,23,59,59), Time.local(2007,3,31,0,0,0).end_of_quarter assert_equal Time.local(2007,12,31,23,59,59), Time.local(2007,12,21,10,10,10).end_of_quarter assert_equal Time.local(2007,6,30,23,59,59), Time.local(2007,4,1,0,0,0).end_of_quarter + assert_equal Time.local(2008,6,30,23,59,59), Time.local(2008,5,31,0,0,0).end_of_quarter end def test_end_of_year @@ -524,13 +525,12 @@ class TimeExtCalculationsTest < Test::Unit::TestCase assert_equal Time.time_with_datetime_fallback(:utc, 2039, 2, 21, 17, 44, 30), DateTime.civil(2039, 2, 21, 17, 44, 30, 0, 0) assert_equal Time.time_with_datetime_fallback(:local, 2039, 2, 21, 17, 44, 30), DateTime.civil(2039, 2, 21, 17, 44, 30, DateTime.local_offset, 0) assert_equal Time.time_with_datetime_fallback(:utc, 1900, 2, 21, 17, 44, 30), DateTime.civil(1900, 2, 21, 17, 44, 30, 0, 0) - assert_equal Time.time_with_datetime_fallback(:local, 1900, 2, 21, 17, 44, 30), DateTime.civil(1900, 2, 21, 17, 44, 30, DateTime.local_offset, 0) assert_equal Time.time_with_datetime_fallback(:utc, 2005), Time.utc(2005) assert_equal Time.time_with_datetime_fallback(:utc, 2039), DateTime.civil(2039, 1, 1, 0, 0, 0, 0, 0) assert_equal Time.time_with_datetime_fallback(:utc, 2005, 2, 21, 17, 44, 30, 1), Time.utc(2005, 2, 21, 17, 44, 30, 1) #with usec # This won't overflow on 64bit linux - expected_to_overflow = Time.time_with_datetime_fallback(:utc, 2039, 2, 21, 17, 44, 30, 1) - unless expected_to_overflow.is_a?(Time) + unless time_is_64bits? + assert_equal Time.time_with_datetime_fallback(:local, 1900, 2, 21, 17, 44, 30), DateTime.civil(1900, 2, 21, 17, 44, 30, DateTime.local_offset, 0) assert_equal Time.time_with_datetime_fallback(:utc, 2039, 2, 21, 17, 44, 30, 1), DateTime.civil(2039, 2, 21, 17, 44, 30, 0, 0) assert_equal ::Date::ITALY, Time.time_with_datetime_fallback(:utc, 2039, 2, 21, 17, 44, 30, 1).start # use Ruby's default start value @@ -546,7 +546,10 @@ class TimeExtCalculationsTest < Test::Unit::TestCase def test_local_time assert_equal Time.local_time(2005, 2, 21, 17, 44, 30), Time.local(2005, 2, 21, 17, 44, 30) assert_equal Time.local_time(2039, 2, 21, 17, 44, 30), DateTime.civil(2039, 2, 21, 17, 44, 30, DateTime.local_offset, 0) - assert_equal Time.local_time(1901, 2, 21, 17, 44, 30), DateTime.civil(1901, 2, 21, 17, 44, 30, DateTime.local_offset, 0) + + unless time_is_64bits? + assert_equal Time.local_time(1901, 2, 21, 17, 44, 30), DateTime.civil(1901, 2, 21, 17, 44, 30, DateTime.local_offset, 0) + end end def test_next_month_on_31st @@ -561,6 +564,74 @@ class TimeExtCalculationsTest < Test::Unit::TestCase assert_nothing_raised { Time.now.xmlschema } end + uses_mocha 'Test Time past?, today? and future?' do + def test_today_with_time_local + Date.stubs(:current).returns(Date.new(2000, 1, 1)) + assert_equal false, Time.local(1999,12,31,23,59,59).today? + assert_equal true, Time.local(2000,1,1,0).today? + assert_equal true, Time.local(2000,1,1,23,59,59).today? + assert_equal false, Time.local(2000,1,2,0).today? + end + + def test_today_with_time_utc + Date.stubs(:current).returns(Date.new(2000, 1, 1)) + assert_equal false, Time.utc(1999,12,31,23,59,59).today? + assert_equal true, Time.utc(2000,1,1,0).today? + assert_equal true, Time.utc(2000,1,1,23,59,59).today? + assert_equal false, Time.utc(2000,1,2,0).today? + end + + def test_past_with_time_current_as_time_local + with_env_tz 'US/Eastern' do + Time.stubs(:current).returns(Time.local(2005,2,10,15,30,45)) + assert_equal true, Time.local(2005,2,10,15,30,44).past? + assert_equal false, Time.local(2005,2,10,15,30,45).past? + assert_equal false, Time.local(2005,2,10,15,30,46).past? + assert_equal true, Time.utc(2005,2,10,20,30,44).past? + assert_equal false, Time.utc(2005,2,10,20,30,45).past? + assert_equal false, Time.utc(2005,2,10,20,30,46).past? + end + end + + def test_past_with_time_current_as_time_with_zone + with_env_tz 'US/Eastern' do + twz = Time.utc(2005,2,10,15,30,45).in_time_zone('Central Time (US & Canada)') + Time.stubs(:current).returns(twz) + assert_equal true, Time.local(2005,2,10,10,30,44).past? + assert_equal false, Time.local(2005,2,10,10,30,45).past? + assert_equal false, Time.local(2005,2,10,10,30,46).past? + assert_equal true, Time.utc(2005,2,10,15,30,44).past? + assert_equal false, Time.utc(2005,2,10,15,30,45).past? + assert_equal false, Time.utc(2005,2,10,15,30,46).past? + end + end + + def test_future_with_time_current_as_time_local + with_env_tz 'US/Eastern' do + Time.stubs(:current).returns(Time.local(2005,2,10,15,30,45)) + assert_equal false, Time.local(2005,2,10,15,30,44).future? + assert_equal false, Time.local(2005,2,10,15,30,45).future? + assert_equal true, Time.local(2005,2,10,15,30,46).future? + assert_equal false, Time.utc(2005,2,10,20,30,44).future? + assert_equal false, Time.utc(2005,2,10,20,30,45).future? + assert_equal true, Time.utc(2005,2,10,20,30,46).future? + end + end + + def test_future_with_time_current_as_time_with_zone + with_env_tz 'US/Eastern' do + twz = Time.utc(2005,2,10,15,30,45).in_time_zone('Central Time (US & Canada)') + Time.stubs(:current).returns(twz) + assert_equal false, Time.local(2005,2,10,10,30,44).future? + assert_equal false, Time.local(2005,2,10,10,30,45).future? + assert_equal true, Time.local(2005,2,10,10,30,46).future? + assert_equal false, Time.utc(2005,2,10,15,30,44).future? + assert_equal false, Time.utc(2005,2,10,15,30,45).future? + assert_equal true, Time.utc(2005,2,10,15,30,46).future? + end + end + end + def test_acts_like_time assert Time.new.acts_like_time? end @@ -624,4 +695,42 @@ class TimeExtCalculationsTest < Test::Unit::TestCase ensure old_tz ? ENV['TZ'] = old_tz : ENV.delete('TZ') end + + def time_is_64bits? + Time.time_with_datetime_fallback(:utc, 2039, 2, 21, 17, 44, 30, 1).is_a?(Time) + end +end + +class TimeExtMarshalingTest < Test::Unit::TestCase + def test_marshaling_with_utc_instance + t = Time.utc(2000) + marshaled = Marshal.dump t + unmarshaled = Marshal.load marshaled + assert_equal t, unmarshaled + assert_equal t.zone, unmarshaled.zone + end + + def test_marshaling_with_local_instance + t = Time.local(2000) + marshaled = Marshal.dump t + unmarshaled = Marshal.load marshaled + assert_equal t, unmarshaled + assert_equal t.zone, unmarshaled.zone + end + + def test_marshaling_with_frozen_utc_instance + t = Time.utc(2000).freeze + marshaled = Marshal.dump t + unmarshaled = Marshal.load marshaled + assert_equal t, unmarshaled + assert_equal t.zone, unmarshaled.zone + end + + def test_marshaling_with_frozen_local_instance + t = Time.local(2000).freeze + marshaled = Marshal.dump t + unmarshaled = Marshal.load marshaled + assert_equal t, unmarshaled + assert_equal t.zone, unmarshaled.zone + end end diff --git a/activesupport/test/core_ext/time_with_zone_test.rb b/activesupport/test/core_ext/time_with_zone_test.rb index dfe04485be..72b540efe0 100644 --- a/activesupport/test/core_ext/time_with_zone_test.rb +++ b/activesupport/test/core_ext/time_with_zone_test.rb @@ -152,6 +152,50 @@ class TimeWithZoneTest < Test::Unit::TestCase assert_equal false, @twz.between?(Time.utc(2000,1,1,0,0,1), Time.utc(2000,1,1,0,0,2)) end + uses_mocha 'TimeWithZone past?, today? and future?' do + def test_today + Date.stubs(:current).returns(Date.new(2000, 1, 1)) + assert_equal false, ActiveSupport::TimeWithZone.new( nil, @time_zone, Time.utc(1999,12,31,23,59,59) ).today? + assert_equal true, ActiveSupport::TimeWithZone.new( nil, @time_zone, Time.utc(2000,1,1,0) ).today? + assert_equal true, ActiveSupport::TimeWithZone.new( nil, @time_zone, Time.utc(2000,1,1,23,59,59) ).today? + assert_equal false, ActiveSupport::TimeWithZone.new( nil, @time_zone, Time.utc(2000,1,2,0) ).today? + end + + def test_past_with_time_current_as_time_local + with_env_tz 'US/Eastern' do + Time.stubs(:current).returns(Time.local(2005,2,10,15,30,45)) + assert_equal true, ActiveSupport::TimeWithZone.new( nil, @time_zone, Time.local(2005,2,10,15,30,44)).past? + assert_equal false, ActiveSupport::TimeWithZone.new( nil, @time_zone, Time.local(2005,2,10,15,30,45)).past? + assert_equal false, ActiveSupport::TimeWithZone.new( nil, @time_zone, Time.local(2005,2,10,15,30,46)).past? + end + end + + def test_past_with_time_current_as_time_with_zone + twz = ActiveSupport::TimeWithZone.new( nil, @time_zone, Time.local(2005,2,10,15,30,45) ) + Time.stubs(:current).returns(twz) + assert_equal true, ActiveSupport::TimeWithZone.new( nil, @time_zone, Time.local(2005,2,10,15,30,44)).past? + assert_equal false, ActiveSupport::TimeWithZone.new( nil, @time_zone, Time.local(2005,2,10,15,30,45)).past? + assert_equal false, ActiveSupport::TimeWithZone.new( nil, @time_zone, Time.local(2005,2,10,15,30,46)).past? + end + + def test_future_with_time_current_as_time_local + with_env_tz 'US/Eastern' do + Time.stubs(:current).returns(Time.local(2005,2,10,15,30,45)) + assert_equal false, ActiveSupport::TimeWithZone.new( nil, @time_zone, Time.local(2005,2,10,15,30,44)).future? + assert_equal false, ActiveSupport::TimeWithZone.new( nil, @time_zone, Time.local(2005,2,10,15,30,45)).future? + assert_equal true, ActiveSupport::TimeWithZone.new( nil, @time_zone, Time.local(2005,2,10,15,30,46)).future? + end + end + + def future_with_time_current_as_time_with_zone + twz = ActiveSupport::TimeWithZone.new( nil, @time_zone, Time.local(2005,2,10,15,30,45) ) + Time.stubs(:current).returns(twz) + assert_equal false, ActiveSupport::TimeWithZone.new( nil, @time_zone, Time.local(2005,2,10,15,30,44)).future? + assert_equal false, ActiveSupport::TimeWithZone.new( nil, @time_zone, Time.local(2005,2,10,15,30,45)).future? + assert_equal true, ActiveSupport::TimeWithZone.new( nil, @time_zone, Time.local(2005,2,10,15,30,46)).future? + end + end + def test_eql? assert @twz.eql?(Time.utc(2000)) assert @twz.eql?( ActiveSupport::TimeWithZone.new(Time.utc(2000), ActiveSupport::TimeZone["Hawaii"]) ) @@ -353,7 +397,7 @@ class TimeWithZoneTest < Test::Unit::TestCase def test_date_part_value_methods silence_warnings do # silence warnings raised by tzinfo gem twz = ActiveSupport::TimeWithZone.new(Time.utc(1999,12,31,19,18,17,500), @time_zone) - twz.stubs(:method_missing).returns(nil) #ensure these methods are defined directly on class + twz.expects(:method_missing).never assert_equal 1999, twz.year assert_equal 12, twz.month assert_equal 31, twz.day @@ -361,6 +405,8 @@ class TimeWithZoneTest < Test::Unit::TestCase assert_equal 18, twz.min assert_equal 17, twz.sec assert_equal 500, twz.usec + assert_equal 5, twz.wday + assert_equal 365, twz.yday end end end @@ -538,7 +584,7 @@ class TimeWithZoneTest < Test::Unit::TestCase assert_equal "Sun, 02 Apr 2006 10:30:01 EDT -04:00", twz.since(1.days + 1.second).inspect assert_equal "Sun, 02 Apr 2006 10:30:01 EDT -04:00", (twz + 1.days + 1.second).inspect end - + def test_advance_1_day_across_spring_dst_transition_backwards twz = ActiveSupport::TimeWithZone.new(nil, @time_zone, Time.utc(2006,4,2,10,30)) # In 2006, spring DST transition occurred Apr 2 at 2AM; this day was only 23 hours long @@ -548,7 +594,7 @@ class TimeWithZoneTest < Test::Unit::TestCase assert_equal "Sat, 01 Apr 2006 10:30:00 EST -05:00", (twz - 1.days).inspect assert_equal "Sat, 01 Apr 2006 10:30:01 EST -05:00", twz.ago(1.days - 1.second).inspect end - + def test_advance_1_day_expressed_as_number_of_seconds_minutes_or_hours_across_spring_dst_transition twz = ActiveSupport::TimeWithZone.new(nil, @time_zone, Time.utc(2006,4,1,10,30)) # In 2006, spring DST transition occurred Apr 2 at 2AM; this day was only 23 hours long @@ -565,7 +611,7 @@ class TimeWithZoneTest < Test::Unit::TestCase assert_equal "Sun, 02 Apr 2006 11:30:00 EDT -04:00", twz.since(24.hours).inspect assert_equal "Sun, 02 Apr 2006 11:30:00 EDT -04:00", twz.advance(:hours => 24).inspect end - + def test_advance_1_day_expressed_as_number_of_seconds_minutes_or_hours_across_spring_dst_transition_backwards twz = ActiveSupport::TimeWithZone.new(nil, @time_zone, Time.utc(2006,4,2,11,30)) # In 2006, spring DST transition occurred Apr 2 at 2AM; this day was only 23 hours long @@ -582,7 +628,7 @@ class TimeWithZoneTest < Test::Unit::TestCase assert_equal "Sat, 01 Apr 2006 10:30:00 EST -05:00", twz.ago(24.hours).inspect assert_equal "Sat, 01 Apr 2006 10:30:00 EST -05:00", twz.advance(:hours => -24).inspect end - + def test_advance_1_day_across_fall_dst_transition twz = ActiveSupport::TimeWithZone.new(nil, @time_zone, Time.utc(2006,10,28,10,30)) # In 2006, fall DST transition occurred Oct 29 at 2AM; this day was 25 hours long @@ -593,7 +639,7 @@ class TimeWithZoneTest < Test::Unit::TestCase assert_equal "Sun, 29 Oct 2006 10:30:01 EST -05:00", twz.since(1.days + 1.second).inspect assert_equal "Sun, 29 Oct 2006 10:30:01 EST -05:00", (twz + 1.days + 1.second).inspect end - + def test_advance_1_day_across_fall_dst_transition_backwards twz = ActiveSupport::TimeWithZone.new(nil, @time_zone, Time.utc(2006,10,29,10,30)) # In 2006, fall DST transition occurred Oct 29 at 2AM; this day was 25 hours long @@ -603,7 +649,7 @@ class TimeWithZoneTest < Test::Unit::TestCase assert_equal "Sat, 28 Oct 2006 10:30:00 EDT -04:00", (twz - 1.days).inspect assert_equal "Sat, 28 Oct 2006 10:30:01 EDT -04:00", twz.ago(1.days - 1.second).inspect end - + def test_advance_1_day_expressed_as_number_of_seconds_minutes_or_hours_across_fall_dst_transition twz = ActiveSupport::TimeWithZone.new(nil, @time_zone, Time.utc(2006,10,28,10,30)) # In 2006, fall DST transition occurred Oct 29 at 2AM; this day was 25 hours long @@ -620,7 +666,7 @@ class TimeWithZoneTest < Test::Unit::TestCase assert_equal "Sun, 29 Oct 2006 09:30:00 EST -05:00", twz.since(24.hours).inspect assert_equal "Sun, 29 Oct 2006 09:30:00 EST -05:00", twz.advance(:hours => 24).inspect end - + def test_advance_1_day_expressed_as_number_of_seconds_minutes_or_hours_across_fall_dst_transition_backwards twz = ActiveSupport::TimeWithZone.new(nil, @time_zone, Time.utc(2006,10,29,9,30)) # In 2006, fall DST transition occurred Oct 29 at 2AM; this day was 25 hours long @@ -669,7 +715,7 @@ class TimeWithZoneTest < Test::Unit::TestCase assert_equal "Sat, 28 Oct 2006 10:30:00 EDT -04:00", twz.ago(1.month).inspect assert_equal "Sat, 28 Oct 2006 10:30:00 EDT -04:00", (twz - 1.month).inspect end - + def test_advance_1_year twz = ActiveSupport::TimeWithZone.new(nil, @time_zone, Time.utc(2008,2,15,10,30)) assert_equal "Sun, 15 Feb 2009 10:30:00 EST -05:00", twz.advance(:years => 1).inspect @@ -679,7 +725,7 @@ class TimeWithZoneTest < Test::Unit::TestCase assert_equal "Thu, 15 Feb 2007 10:30:00 EST -05:00", twz.years_ago(1).inspect assert_equal "Thu, 15 Feb 2007 10:30:00 EST -05:00", (twz - 1.year).inspect end - + def test_advance_1_year_during_dst twz = ActiveSupport::TimeWithZone.new(nil, @time_zone, Time.utc(2008,7,15,10,30)) assert_equal "Wed, 15 Jul 2009 10:30:00 EDT -04:00", twz.advance(:years => 1).inspect @@ -689,6 +735,14 @@ class TimeWithZoneTest < Test::Unit::TestCase assert_equal "Sun, 15 Jul 2007 10:30:00 EDT -04:00", twz.years_ago(1).inspect assert_equal "Sun, 15 Jul 2007 10:30:00 EDT -04:00", (twz - 1.year).inspect end + + protected + def with_env_tz(new_tz = 'US/Eastern') + old_tz, ENV['TZ'] = ENV['TZ'], new_tz + yield + ensure + old_tz ? ENV['TZ'] = old_tz : ENV.delete('TZ') + end end class TimeWithZoneMethodsForTimeAndDateTimeTest < Test::Unit::TestCase diff --git a/activesupport/test/dependencies_test.rb b/activesupport/test/dependencies_test.rb index 39c9c74c94..18ad784837 100644 --- a/activesupport/test/dependencies_test.rb +++ b/activesupport/test/dependencies_test.rb @@ -146,42 +146,42 @@ class DependenciesTest < Test::Unit::TestCase def test_directories_manifest_as_modules_unless_const_defined with_loading 'autoloading_fixtures' do assert_kind_of Module, ModuleFolder - Object.send! :remove_const, :ModuleFolder + Object.__send__ :remove_const, :ModuleFolder end end def test_module_with_nested_class with_loading 'autoloading_fixtures' do assert_kind_of Class, ModuleFolder::NestedClass - Object.send! :remove_const, :ModuleFolder + Object.__send__ :remove_const, :ModuleFolder end end def test_module_with_nested_inline_class with_loading 'autoloading_fixtures' do assert_kind_of Class, ModuleFolder::InlineClass - Object.send! :remove_const, :ModuleFolder + Object.__send__ :remove_const, :ModuleFolder end end def test_directories_may_manifest_as_nested_classes with_loading 'autoloading_fixtures' do assert_kind_of Class, ClassFolder - Object.send! :remove_const, :ClassFolder + Object.__send__ :remove_const, :ClassFolder end end def test_class_with_nested_class with_loading 'autoloading_fixtures' do assert_kind_of Class, ClassFolder::NestedClass - Object.send! :remove_const, :ClassFolder + Object.__send__ :remove_const, :ClassFolder end end def test_class_with_nested_inline_class with_loading 'autoloading_fixtures' do assert_kind_of Class, ClassFolder::InlineClass - Object.send! :remove_const, :ClassFolder + Object.__send__ :remove_const, :ClassFolder end end @@ -190,7 +190,7 @@ class DependenciesTest < Test::Unit::TestCase assert_kind_of Class, ClassFolder::ClassFolderSubclass assert_kind_of Class, ClassFolder assert_equal 'indeed', ClassFolder::ClassFolderSubclass::ConstantInClassFolder - Object.send! :remove_const, :ClassFolder + Object.__send__ :remove_const, :ClassFolder end end @@ -199,7 +199,7 @@ class DependenciesTest < Test::Unit::TestCase sibling = ModuleFolder::NestedClass.class_eval "NestedSibling" assert defined?(ModuleFolder::NestedSibling) assert_equal ModuleFolder::NestedSibling, sibling - Object.send! :remove_const, :ModuleFolder + Object.__send__ :remove_const, :ModuleFolder end end @@ -208,7 +208,7 @@ class DependenciesTest < Test::Unit::TestCase assert ! defined?(ModuleFolder) assert_raises(NameError) { ModuleFolder::Object } assert_raises(NameError) { ModuleFolder::NestedClass::Object } - Object.send! :remove_const, :ModuleFolder + Object.__send__ :remove_const, :ModuleFolder end end diff --git a/activesupport/test/inflector_test.rb b/activesupport/test/inflector_test.rb index 8eebe1be25..f304844e82 100644 --- a/activesupport/test/inflector_test.rb +++ b/activesupport/test/inflector_test.rb @@ -98,6 +98,12 @@ class InflectorTest < Test::Unit::TestCase end end + def test_parameterize + StringToParameterized.each do |some_string, parameterized_string| + assert_equal(parameterized_string, ActiveSupport::Inflector.parameterize(some_string)) + end + end + def test_classify ClassNameToTableName.each do |class_name, table_name| assert_equal(class_name, ActiveSupport::Inflector.classify(table_name)) diff --git a/activesupport/test/inflector_test_cases.rb b/activesupport/test/inflector_test_cases.rb index a9dd83a389..8057809dbd 100644 --- a/activesupport/test/inflector_test_cases.rb +++ b/activesupport/test/inflector_test_cases.rb @@ -142,6 +142,14 @@ module InflectorTestCases "NodeChild" => "node_children" } + StringToParameterized = { + "Donald E. Knuth" => "donald-e-knuth", + "Random text with *(bad)* characters" => "random-text-with-bad-characters", + "Malmö" => "malmo", + "Garçons" => "garcons", + "Allow_Under_Scores" => "allow_under_scores" + } + UnderscoreToHuman = { "employee_salary" => "Employee salary", "employee_id" => "Employee", diff --git a/activesupport/test/multibyte_chars_test.rb b/activesupport/test/multibyte_chars_test.rb index 63cfadb7ec..a87309b038 100644 --- a/activesupport/test/multibyte_chars_test.rb +++ b/activesupport/test/multibyte_chars_test.rb @@ -167,6 +167,10 @@ class CharsTest < Test::Unit::TestCase assert_equal false, 'test'.chars.respond_to?(:a_method_that_doesnt_exist) end + def test_acts_like_string + assert 'Bambi'.chars.acts_like_string? + end + protected def with_kcode(kcode) diff --git a/activesupport/test/secure_random_test.rb b/activesupport/test/secure_random_test.rb new file mode 100644 index 0000000000..b0b6c21a81 --- /dev/null +++ b/activesupport/test/secure_random_test.rb @@ -0,0 +1,15 @@ +require 'abstract_unit' + +class SecureRandomTest < Test::Unit::TestCase + def test_random_bytes + b1 = ActiveSupport::SecureRandom.random_bytes(64) + b2 = ActiveSupport::SecureRandom.random_bytes(64) + assert_not_equal b1, b2 + end + + def test_hex + b1 = ActiveSupport::SecureRandom.hex(64) + b2 = ActiveSupport::SecureRandom.hex(64) + assert_not_equal b1, b2 + end +end |