diff options
Diffstat (limited to 'activesupport/lib/active_support')
126 files changed, 2898 insertions, 3233 deletions
diff --git a/activesupport/lib/active_support/base64.rb b/activesupport/lib/active_support/base64.rb index acb8e5a967..b8c01628ce 100644 --- a/activesupport/lib/active_support/base64.rb +++ b/activesupport/lib/active_support/base64.rb @@ -30,4 +30,13 @@ module ActiveSupport end end end + + # Encodes the value as base64 without the newline breaks. This makes the base64 encoding readily usable as URL parameters + # or memcache keys without further processing. + # + # ActiveSupport::Base64.encode64s("Original unencoded string") + # # => "T3JpZ2luYWwgdW5lbmNvZGVkIHN0cmluZw==" + def Base64.encode64s(value) + encode64(value).gsub(/\n/, '') + end end diff --git a/activesupport/lib/active_support/basic_object.rb b/activesupport/lib/active_support/basic_object.rb index 1f77209e7f..3b5277c205 100644 --- a/activesupport/lib/active_support/basic_object.rb +++ b/activesupport/lib/active_support/basic_object.rb @@ -1,13 +1,7 @@ -# A base class with no predefined methods that tries to behave like Builder's -# BlankSlate in Ruby 1.9. In Ruby pre-1.9, this is actually the -# Builder::BlankSlate class. -# -# Ruby 1.9 introduces BasicObject which differs slightly from Builder's -# BlankSlate that has been used so far. ActiveSupport::BasicObject provides a -# barebones base class that emulates Builder::BlankSlate while still relying on -# Ruby 1.9's BasicObject in Ruby 1.9. module ActiveSupport if defined? ::BasicObject + # A class with no predefined methods that behaves similarly to Builder's + # BlankSlate. Used for proxy classes. class BasicObject < ::BasicObject undef_method :== undef_method :equal? @@ -18,7 +12,10 @@ module ActiveSupport end end else - require 'blankslate' - BasicObject = BlankSlate + class BasicObject #:nodoc: + instance_methods.each do |m| + undef_method(m) if m.to_s !~ /(?:^__|^nil\?$|^send$|^object_id$)/ + end + end end end diff --git a/activesupport/lib/active_support/buffered_logger.rb b/activesupport/lib/active_support/buffered_logger.rb index 568c596f91..ee66479dde 100644 --- a/activesupport/lib/active_support/buffered_logger.rb +++ b/activesupport/lib/active_support/buffered_logger.rb @@ -1,3 +1,5 @@ +require 'active_support/core_ext/class/attribute_accessors' + module ActiveSupport # Inspired by the buffered logger idea by Ezra class BufferedLogger diff --git a/activesupport/lib/active_support/cache.rb b/activesupport/lib/active_support/cache.rb index 83174d3a85..4b2eebb007 100644 --- a/activesupport/lib/active_support/cache.rb +++ b/activesupport/lib/active_support/cache.rb @@ -1,4 +1,7 @@ require 'benchmark' +require 'active_support/core_ext/benchmark' +require 'active_support/core_ext/exception' +require 'active_support/core_ext/class/attribute_accessors' module ActiveSupport # See ActiveSupport::Cache::Store for documentation. @@ -6,7 +9,6 @@ module ActiveSupport autoload :FileStore, 'active_support/cache/file_store' autoload :MemoryStore, 'active_support/cache/memory_store' autoload :SynchronizedMemoryStore, 'active_support/cache/synchronized_memory_store' - autoload :DRbStore, 'active_support/cache/drb_store' autoload :MemCacheStore, 'active_support/cache/mem_cache_store' autoload :CompressedMemCacheStore, 'active_support/cache/compressed_mem_cache_store' @@ -26,8 +28,8 @@ module ActiveSupport # ActiveSupport::Cache.lookup_store(:memory_store) # # => returns a new ActiveSupport::Cache::MemoryStore object # - # ActiveSupport::Cache.lookup_store(:drb_store) - # # => returns a new ActiveSupport::Cache::DRbStore object + # ActiveSupport::Cache.lookup_store(:mem_cache_store) + # # => returns a new ActiveSupport::Cache::MemCacheStore object # # Any additional arguments will be passed to the corresponding cache store # class's constructor: @@ -44,7 +46,7 @@ module ActiveSupport case store when Symbol - store_class_name = (store == :drb_store ? "DRbStore" : store.to_s.camelize) + store_class_name = store.to_s.camelize store_class = ActiveSupport::Cache.const_get(store_class_name) store_class.new(*parameters) when nil diff --git a/activesupport/lib/active_support/cache/drb_store.rb b/activesupport/lib/active_support/cache/drb_store.rb deleted file mode 100644 index b16ed25aa3..0000000000 --- a/activesupport/lib/active_support/cache/drb_store.rb +++ /dev/null @@ -1,14 +0,0 @@ -module ActiveSupport - module Cache - class DRbStore < MemoryStore #:nodoc: - attr_reader :address - - def initialize(address = 'druby://localhost:9192') - require 'drb' unless defined?(DRbObject) - super() - @address = address - @data = DRbObject.new(nil, address) - end - end - end -end diff --git a/activesupport/lib/active_support/cache/file_store.rb b/activesupport/lib/active_support/cache/file_store.rb index 7f34cb52a1..3217350d58 100644 --- a/activesupport/lib/active_support/cache/file_store.rb +++ b/activesupport/lib/active_support/cache/file_store.rb @@ -1,3 +1,5 @@ +require 'active_support/core_ext/file/atomic' + module ActiveSupport module Cache # A cache store implementation which stores everything on the filesystem. diff --git a/activesupport/lib/active_support/core.rb b/activesupport/lib/active_support/core.rb new file mode 100644 index 0000000000..ad8db94941 --- /dev/null +++ b/activesupport/lib/active_support/core.rb @@ -0,0 +1,7 @@ +module ActiveSupport + module Core + def self.load_all! + [TimeWithZone] + end + end +end diff --git a/activesupport/lib/active_support/core/all.rb b/activesupport/lib/active_support/core/all.rb new file mode 100644 index 0000000000..f397f48e9c --- /dev/null +++ b/activesupport/lib/active_support/core/all.rb @@ -0,0 +1,4 @@ +require 'active_support/core' +Dir["#{File.dirname(__FILE__)}/*.rb"].sort.each do |path| + require "active_support/core/#{File.basename(path, '.rb')}" +end diff --git a/activesupport/lib/active_support/core/time.rb b/activesupport/lib/active_support/core/time.rb new file mode 100644 index 0000000000..43e13b5e58 --- /dev/null +++ b/activesupport/lib/active_support/core/time.rb @@ -0,0 +1,4 @@ +require 'active_support/core/time/autoload' +require 'active_support/core_ext/time' +require 'active_support/core_ext/date' +require 'active_support/core_ext/date_time' diff --git a/activesupport/lib/active_support/core/time/autoload.rb b/activesupport/lib/active_support/core/time/autoload.rb new file mode 100644 index 0000000000..c9a7731b39 --- /dev/null +++ b/activesupport/lib/active_support/core/time/autoload.rb @@ -0,0 +1,5 @@ +module ActiveSupport + autoload :Duration, 'active_support/duration' + autoload :TimeWithZone, 'active_support/time_with_zone' + autoload :TimeZone, 'active_support/values/time_zone' +end diff --git a/activesupport/lib/active_support/core_ext.rb b/activesupport/lib/active_support/core_ext.rb index f2f976df9b..46a8609dd7 100644 --- a/activesupport/lib/active_support/core_ext.rb +++ b/activesupport/lib/active_support/core_ext.rb @@ -1,4 +1,3 @@ -Dir[File.dirname(__FILE__) + "/core_ext/*.rb"].sort.each do |path| - filename = File.basename(path, '.rb') - require "active_support/core_ext/#{filename}" +Dir["#{File.dirname(__FILE__)}/core_ext/*.rb"].sort.each do |path| + require "active_support/core_ext/#{File.basename(path, '.rb')}" end diff --git a/activesupport/lib/active_support/core_ext/array.rb b/activesupport/lib/active_support/core_ext/array.rb index 82c6b1243a..b583c7533e 100644 --- a/activesupport/lib/active_support/core_ext/array.rb +++ b/activesupport/lib/active_support/core_ext/array.rb @@ -1,15 +1,6 @@ +require 'active_support/core_ext/array/wrap' require 'active_support/core_ext/array/access' require 'active_support/core_ext/array/conversions' require 'active_support/core_ext/array/extract_options' require 'active_support/core_ext/array/grouping' require 'active_support/core_ext/array/random_access' -require 'active_support/core_ext/array/wrapper' - -class Array #:nodoc: - include ActiveSupport::CoreExtensions::Array::Access - include ActiveSupport::CoreExtensions::Array::Conversions - include ActiveSupport::CoreExtensions::Array::ExtractOptions - include ActiveSupport::CoreExtensions::Array::Grouping - include ActiveSupport::CoreExtensions::Array::RandomAccess - extend ActiveSupport::CoreExtensions::Array::Wrapper -end diff --git a/activesupport/lib/active_support/core_ext/array/access.rb b/activesupport/lib/active_support/core_ext/array/access.rb index 6de338bfcc..d7606bb9b0 100644 --- a/activesupport/lib/active_support/core_ext/array/access.rb +++ b/activesupport/lib/active_support/core_ext/array/access.rb @@ -1,53 +1,46 @@ -module ActiveSupport #:nodoc: - module CoreExtensions #:nodoc: - module Array #:nodoc: - # Makes it easier to access parts of an array. - module Access - # Returns the tail of the array from +position+. - # - # %w( a b c d ).from(0) # => %w( a b c d ) - # %w( a b c d ).from(2) # => %w( c d ) - # %w( a b c d ).from(10) # => nil - # %w().from(0) # => nil - def from(position) - self[position..-1] - end - - # Returns the beginning of the array up to +position+. - # - # %w( a b c d ).to(0) # => %w( a ) - # %w( a b c d ).to(2) # => %w( a b c ) - # %w( a b c d ).to(10) # => %w( a b c d ) - # %w().to(0) # => %w() - def to(position) - self[0..position] - end +class Array + # Returns the tail of the array from +position+. + # + # %w( a b c d ).from(0) # => %w( a b c d ) + # %w( a b c d ).from(2) # => %w( c d ) + # %w( a b c d ).from(10) # => nil + # %w().from(0) # => nil + def from(position) + self[position..-1] + end - # Equal to <tt>self[1]</tt>. - def second - self[1] - end + # Returns the beginning of the array up to +position+. + # + # %w( a b c d ).to(0) # => %w( a ) + # %w( a b c d ).to(2) # => %w( a b c ) + # %w( a b c d ).to(10) # => %w( a b c d ) + # %w().to(0) # => %w() + def to(position) + self[0..position] + end - # Equal to <tt>self[2]</tt>. - def third - self[2] - end + # Equal to <tt>self[1]</tt>. + def second + self[1] + end - # Equal to <tt>self[3]</tt>. - def fourth - self[3] - end + # Equal to <tt>self[2]</tt>. + def third + self[2] + end - # Equal to <tt>self[4]</tt>. - def fifth - self[4] - end + # Equal to <tt>self[3]</tt>. + def fourth + self[3] + end + + # Equal to <tt>self[4]</tt>. + def fifth + self[4] + end - # Equal to <tt>self[41]</tt>. Also known as accessing "the reddit". - def forty_two - self[41] - end - end - end + # Equal to <tt>self[41]</tt>. Also known as accessing "the reddit". + def forty_two + self[41] end end diff --git a/activesupport/lib/active_support/core_ext/array/conversions.rb b/activesupport/lib/active_support/core_ext/array/conversions.rb index ba8e022fb2..81e466779e 100644 --- a/activesupport/lib/active_support/core_ext/array/conversions.rb +++ b/activesupport/lib/active_support/core_ext/array/conversions.rb @@ -1,196 +1,184 @@ -module ActiveSupport #:nodoc: - module CoreExtensions #:nodoc: - module Array #:nodoc: - module Conversions - # Converts the array to a comma-separated sentence where the last element is joined by the connector word. Options: - # * <tt>:words_connector</tt> - The sign or word used to join the elements in arrays with two or more elements (default: ", ") - # * <tt>:two_words_connector</tt> - The sign or word used to join the elements in arrays with two elements (default: " and ") - # * <tt>:last_word_connector</tt> - The sign or word used to join the last element in arrays with three or more elements (default: ", and ") - def to_sentence(options = {}) - default_words_connector = I18n.translate(:'support.array.words_connector', :locale => options[:locale]) - default_two_words_connector = I18n.translate(:'support.array.two_words_connector', :locale => options[:locale]) - default_last_word_connector = I18n.translate(:'support.array.last_word_connector', :locale => options[:locale]) +class Array + # Converts the array to a comma-separated sentence where the last element is joined by the connector word. Options: + # * <tt>:words_connector</tt> - The sign or word used to join the elements in arrays with two or more elements (default: ", ") + # * <tt>:two_words_connector</tt> - The sign or word used to join the elements in arrays with two elements (default: " and ") + # * <tt>:last_word_connector</tt> - The sign or word used to join the last element in arrays with three or more elements (default: ", and ") + def to_sentence(options = {}) + default_words_connector = I18n.translate(:'support.array.words_connector', :locale => options[:locale]) + default_two_words_connector = I18n.translate(:'support.array.two_words_connector', :locale => options[:locale]) + default_last_word_connector = I18n.translate(:'support.array.last_word_connector', :locale => options[:locale]) - # Try to emulate to_senteces previous to 2.3 - if options.has_key?(:connector) || options.has_key?(:skip_last_comma) - ::ActiveSupport::Deprecation.warn(":connector has been deprecated. Use :words_connector instead", caller) if options.has_key? :connector - ::ActiveSupport::Deprecation.warn(":skip_last_comma has been deprecated. Use :last_word_connector instead", caller) if options.has_key? :skip_last_comma + # Try to emulate to_senteces previous to 2.3 + if options.has_key?(:connector) || options.has_key?(:skip_last_comma) + ::ActiveSupport::Deprecation.warn(":connector has been deprecated. Use :words_connector instead", caller) if options.has_key? :connector + ::ActiveSupport::Deprecation.warn(":skip_last_comma has been deprecated. Use :last_word_connector instead", caller) if options.has_key? :skip_last_comma - skip_last_comma = options.delete :skip_last_comma - if connector = options.delete(:connector) - options[:last_word_connector] ||= skip_last_comma ? connector : ", #{connector}" - else - options[:last_word_connector] ||= skip_last_comma ? default_two_words_connector : default_last_word_connector - end - end - - options.assert_valid_keys(:words_connector, :two_words_connector, :last_word_connector, :locale) - options.reverse_merge! :words_connector => default_words_connector, :two_words_connector => default_two_words_connector, :last_word_connector => default_last_word_connector - - case length - when 0 - "" - when 1 - self[0].to_s - when 2 - "#{self[0]}#{options[:two_words_connector]}#{self[1]}" - else - "#{self[0...-1].join(options[:words_connector])}#{options[:last_word_connector]}#{self[-1]}" - end - end - + skip_last_comma = options.delete :skip_last_comma + if connector = options.delete(:connector) + options[:last_word_connector] ||= skip_last_comma ? connector : ", #{connector}" + else + options[:last_word_connector] ||= skip_last_comma ? default_two_words_connector : default_last_word_connector + end + end - # Calls <tt>to_param</tt> on all its elements and joins the result with - # slashes. This is used by <tt>url_for</tt> in Action Pack. - def to_param - collect { |e| e.to_param }.join '/' - end + options.assert_valid_keys(:words_connector, :two_words_connector, :last_word_connector, :locale) + options.reverse_merge! :words_connector => default_words_connector, :two_words_connector => default_two_words_connector, :last_word_connector => default_last_word_connector - # Converts an array into a string suitable for use as a URL query string, - # using the given +key+ as the param name. - # - # ['Rails', 'coding'].to_query('hobbies') # => "hobbies%5B%5D=Rails&hobbies%5B%5D=coding" - def to_query(key) - prefix = "#{key}[]" - collect { |value| value.to_query(prefix) }.join '&' - end + case length + when 0 + "" + when 1 + self[0].to_s + when 2 + "#{self[0]}#{options[:two_words_connector]}#{self[1]}" + else + "#{self[0...-1].join(options[:words_connector])}#{options[:last_word_connector]}#{self[-1]}" + end + end - def self.included(base) #:nodoc: - base.class_eval do - alias_method :to_default_s, :to_s - alias_method :to_s, :to_formatted_s - end - end - # Converts a collection of elements into a formatted string by calling - # <tt>to_s</tt> on all elements and joining them: - # - # Blog.find(:all).to_formatted_s # => "First PostSecond PostThird Post" - # - # Adding in the <tt>:db</tt> argument as the format yields a prettier - # output: - # - # Blog.find(:all).to_formatted_s(:db) # => "First Post,Second Post,Third Post" - def to_formatted_s(format = :default) - case format - when :db - if respond_to?(:empty?) && self.empty? - "null" - else - collect { |element| element.id }.join(",") - end - else - to_default_s - end - end + # Calls <tt>to_param</tt> on all its elements and joins the result with + # slashes. This is used by <tt>url_for</tt> in Action Pack. + def to_param + collect { |e| e.to_param }.join '/' + end - # Returns a string that represents this array in XML by sending +to_xml+ - # to each element. Active Record collections delegate their representation - # in XML to this method. - # - # All elements are expected to respond to +to_xml+, if any of them does - # not an exception is raised. - # - # The root node reflects the class name of the first element in plural - # if all elements belong to the same type and that's not Hash: - # - # customer.projects.to_xml - # - # <?xml version="1.0" encoding="UTF-8"?> - # <projects type="array"> - # <project> - # <amount type="decimal">20000.0</amount> - # <customer-id type="integer">1567</customer-id> - # <deal-date type="date">2008-04-09</deal-date> - # ... - # </project> - # <project> - # <amount type="decimal">57230.0</amount> - # <customer-id type="integer">1567</customer-id> - # <deal-date type="date">2008-04-15</deal-date> - # ... - # </project> - # </projects> - # - # Otherwise the root element is "records": - # - # [{:foo => 1, :bar => 2}, {:baz => 3}].to_xml - # - # <?xml version="1.0" encoding="UTF-8"?> - # <records type="array"> - # <record> - # <bar type="integer">2</bar> - # <foo type="integer">1</foo> - # </record> - # <record> - # <baz type="integer">3</baz> - # </record> - # </records> - # - # If the collection is empty the root element is "nil-classes" by default: - # - # [].to_xml - # - # <?xml version="1.0" encoding="UTF-8"?> - # <nil-classes type="array"/> - # - # To ensure a meaningful root element use the <tt>:root</tt> option: - # - # customer_with_no_projects.projects.to_xml(:root => "projects") - # - # <?xml version="1.0" encoding="UTF-8"?> - # <projects type="array"/> - # - # By default root children have as node name the one of the root - # singularized. You can change it with the <tt>:children</tt> option. - # - # The +options+ hash is passed downwards: - # - # Message.all.to_xml(:skip_types => true) - # - # <?xml version="1.0" encoding="UTF-8"?> - # <messages> - # <message> - # <created-at>2008-03-07T09:58:18+01:00</created-at> - # <id>1</id> - # <name>1</name> - # <updated-at>2008-03-07T09:58:18+01:00</updated-at> - # <user-id>1</user-id> - # </message> - # </messages> - # - def to_xml(options = {}) - raise "Not all elements respond to to_xml" unless all? { |e| e.respond_to? :to_xml } - require 'builder' unless defined?(Builder) + # Converts an array into a string suitable for use as a URL query string, + # using the given +key+ as the param name. + # + # ['Rails', 'coding'].to_query('hobbies') # => "hobbies%5B%5D=Rails&hobbies%5B%5D=coding" + def to_query(key) + prefix = "#{key}[]" + collect { |value| value.to_query(prefix) }.join '&' + end - options[:root] ||= all? { |e| e.is_a?(first.class) && first.class.to_s != "Hash" } ? first.class.to_s.underscore.pluralize : "records" - options[:children] ||= options[:root].singularize - options[:indent] ||= 2 - options[:builder] ||= Builder::XmlMarkup.new(:indent => options[:indent]) + # Converts a collection of elements into a formatted string by calling + # <tt>to_s</tt> on all elements and joining them: + # + # Blog.find(:all).to_formatted_s # => "First PostSecond PostThird Post" + # + # Adding in the <tt>:db</tt> argument as the format yields a prettier + # output: + # + # Blog.find(:all).to_formatted_s(:db) # => "First Post,Second Post,Third Post" + def to_formatted_s(format = :default) + case format + when :db + if respond_to?(:empty?) && self.empty? + "null" + else + collect { |element| element.id }.join(",") + end + else + to_default_s + end + end + alias_method :to_default_s, :to_s + alias_method :to_s, :to_formatted_s - root = options.delete(:root).to_s - children = options.delete(:children) + # Returns a string that represents this array in XML by sending +to_xml+ + # to each element. Active Record collections delegate their representation + # in XML to this method. + # + # All elements are expected to respond to +to_xml+, if any of them does + # not an exception is raised. + # + # The root node reflects the class name of the first element in plural + # if all elements belong to the same type and that's not Hash: + # + # customer.projects.to_xml + # + # <?xml version="1.0" encoding="UTF-8"?> + # <projects type="array"> + # <project> + # <amount type="decimal">20000.0</amount> + # <customer-id type="integer">1567</customer-id> + # <deal-date type="date">2008-04-09</deal-date> + # ... + # </project> + # <project> + # <amount type="decimal">57230.0</amount> + # <customer-id type="integer">1567</customer-id> + # <deal-date type="date">2008-04-15</deal-date> + # ... + # </project> + # </projects> + # + # Otherwise the root element is "records": + # + # [{:foo => 1, :bar => 2}, {:baz => 3}].to_xml + # + # <?xml version="1.0" encoding="UTF-8"?> + # <records type="array"> + # <record> + # <bar type="integer">2</bar> + # <foo type="integer">1</foo> + # </record> + # <record> + # <baz type="integer">3</baz> + # </record> + # </records> + # + # If the collection is empty the root element is "nil-classes" by default: + # + # [].to_xml + # + # <?xml version="1.0" encoding="UTF-8"?> + # <nil-classes type="array"/> + # + # To ensure a meaningful root element use the <tt>:root</tt> option: + # + # customer_with_no_projects.projects.to_xml(:root => "projects") + # + # <?xml version="1.0" encoding="UTF-8"?> + # <projects type="array"/> + # + # By default root children have as node name the one of the root + # singularized. You can change it with the <tt>:children</tt> option. + # + # The +options+ hash is passed downwards: + # + # Message.all.to_xml(:skip_types => true) + # + # <?xml version="1.0" encoding="UTF-8"?> + # <messages> + # <message> + # <created-at>2008-03-07T09:58:18+01:00</created-at> + # <id>1</id> + # <name>1</name> + # <updated-at>2008-03-07T09:58:18+01:00</updated-at> + # <user-id>1</user-id> + # </message> + # </messages> + # + def to_xml(options = {}) + raise "Not all elements respond to to_xml" unless all? { |e| e.respond_to? :to_xml } + require 'builder' unless defined?(Builder) - if !options.has_key?(:dasherize) || options[:dasherize] - root = root.dasherize - end + options[:root] ||= all? { |e| e.is_a?(first.class) && first.class.to_s != "Hash" } ? first.class.to_s.underscore.pluralize : "records" + options[:children] ||= options[:root].singularize + options[:indent] ||= 2 + options[:builder] ||= Builder::XmlMarkup.new(:indent => options[:indent]) - options[:builder].instruct! unless options.delete(:skip_instruct) + root = options.delete(:root).to_s + children = options.delete(:children) - opts = options.merge({ :root => children }) + if !options.has_key?(:dasherize) || options[:dasherize] + root = root.dasherize + end - xml = options[:builder] - if empty? - xml.tag!(root, options[:skip_types] ? {} : {:type => "array"}) - else - xml.tag!(root, options[:skip_types] ? {} : {:type => "array"}) { - yield xml if block_given? - each { |e| e.to_xml(opts.merge({ :skip_instruct => true })) } - } - end - end + options[:builder].instruct! unless options.delete(:skip_instruct) - end + opts = options.merge({ :root => children }) + + xml = options[:builder] + if empty? + xml.tag!(root, options[:skip_types] ? {} : {:type => "array"}) + else + xml.tag!(root, options[:skip_types] ? {} : {:type => "array"}) { + yield xml if block_given? + each { |e| e.to_xml(opts.merge({ :skip_instruct => true })) } + } end end end diff --git a/activesupport/lib/active_support/core_ext/array/extract_options.rb b/activesupport/lib/active_support/core_ext/array/extract_options.rb index eb917576d7..9ca32dc7aa 100644 --- a/activesupport/lib/active_support/core_ext/array/extract_options.rb +++ b/activesupport/lib/active_support/core_ext/array/extract_options.rb @@ -1,20 +1,14 @@ -module ActiveSupport #:nodoc: - module CoreExtensions #:nodoc: - module Array #:nodoc: - module ExtractOptions - # Extracts options from a set of arguments. Removes and returns the last - # element in the array if it's a hash, otherwise returns a blank hash. - # - # def options(*args) - # args.extract_options! - # end - # - # options(1, 2) # => {} - # options(1, 2, :a => :b) # => {:a=>:b} - def extract_options! - last.is_a?(::Hash) ? pop : {} - end - end - end +class Array + # Extracts options from a set of arguments. Removes and returns the last + # element in the array if it's a hash, otherwise returns a blank hash. + # + # def options(*args) + # args.extract_options! + # end + # + # options(1, 2) # => {} + # options(1, 2, :a => :b) # => {:a=>:b} + def extract_options! + last.is_a?(::Hash) ? pop : {} 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 f782f8facf..ef416787a9 100644 --- a/activesupport/lib/active_support/core_ext/array/grouping.rb +++ b/activesupport/lib/active_support/core_ext/array/grouping.rb @@ -1,106 +1,100 @@ require 'enumerator' -module ActiveSupport #:nodoc: - module CoreExtensions #:nodoc: - module Array #:nodoc: - module Grouping - # 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) {|group| p group} - # ["1", "2", "3"] - # ["4", "5", "6"] - # ["7", nil, nil] - # - # %w(1 2 3).in_groups_of(2, ' ') {|group| p group} - # ["1", "2"] - # ["3", " "] - # - # %w(1 2 3).in_groups_of(2, false) {|group| p group} - # ["1", "2"] - # ["3"] - def in_groups_of(number, fill_with = nil) - if fill_with == false - collection = self - else - # size % number gives how many extra we have; - # subtracting from number gives how many to add; - # modulo number ensures we don't add group of just fill. - padding = (number - size % number) % number - collection = dup.concat([fill_with] * padding) - end - - if block_given? - collection.each_slice(number) { |slice| yield(slice) } - else - returning [] do |groups| - collection.each_slice(number) { |group| groups << group } - end - end - end +class Array + # 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) {|group| p group} + # ["1", "2", "3"] + # ["4", "5", "6"] + # ["7", nil, nil] + # + # %w(1 2 3).in_groups_of(2, ' ') {|group| p group} + # ["1", "2"] + # ["3", " "] + # + # %w(1 2 3).in_groups_of(2, false) {|group| p group} + # ["1", "2"] + # ["3"] + def in_groups_of(number, fill_with = nil) + if fill_with == false + collection = self + else + # size % number gives how many extra we have; + # subtracting from number gives how many to add; + # modulo number ensures we don't add group of just fill. + padding = (number - size % number) % number + collection = dup.concat([fill_with] * padding) + end - # 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) {|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, ' ') {|group| p group} - # ["1", "2", "3"] - # ["4", "5", " "] - # ["6", "7", " "] - # - # %w(1 2 3 4 5 6 7).in_groups(3, false) {|group| p group} - # ["1", "2", "3"] - # ["4", "5"] - # ["6", "7"] - def in_groups(number, fill_with = nil) - # size / number gives minor group size; - # size % number gives how many objects need extra accomodation; - # each group hold either division or division + 1 items. - division = size / number - modulo = size % number + if block_given? + collection.each_slice(number) { |slice| yield(slice) } + else + groups = [] + collection.each_slice(number) { |group| groups << group } + groups + end + end - # create a new array avoiding dup - groups = [] - start = 0 + # 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) {|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, ' ') {|group| p group} + # ["1", "2", "3"] + # ["4", "5", " "] + # ["6", "7", " "] + # + # %w(1 2 3 4 5 6 7).in_groups(3, false) {|group| p group} + # ["1", "2", "3"] + # ["4", "5"] + # ["6", "7"] + def in_groups(number, fill_with = nil) + # size / number gives minor group size; + # size % number gives how many objects need extra accomodation; + # each group hold either division or division + 1 items. + division = size / number + modulo = size % number - number.times do |index| - length = division + (modulo > 0 && modulo > index ? 1 : 0) - padding = fill_with != false && - modulo > 0 && length == division ? 1 : 0 - groups << slice(start, length).concat([fill_with] * padding) - start += length - end + # create a new array avoiding dup + groups = [] + start = 0 - if block_given? - groups.each{|g| yield(g) } - else - groups - end - end + number.times do |index| + length = division + (modulo > 0 && modulo > index ? 1 : 0) + padding = fill_with != false && + modulo > 0 && length == division ? 1 : 0 + groups << slice(start, length).concat([fill_with] * padding) + start += length + end - # Divides the array into one or more subarrays based on a delimiting +value+ - # or the result of an optional block. - # - # [1, 2, 3, 4, 5].split(3) # => [[1, 2], [4, 5]] - # (1..10).to_a.split { |i| i % 3 == 0 } # => [[1, 2], [4, 5], [7, 8], [10]] - def split(value = nil) - using_block = block_given? + if block_given? + groups.each { |g| yield(g) } + else + groups + end + end - inject([[]]) do |results, element| - if (using_block && yield(element)) || (value == element) - results << [] - else - results.last << element - end + # Divides the array into one or more subarrays based on a delimiting +value+ + # or the result of an optional block. + # + # [1, 2, 3, 4, 5].split(3) # => [[1, 2], [4, 5]] + # (1..10).to_a.split { |i| i % 3 == 0 } # => [[1, 2], [4, 5], [7, 8], [10]] + def split(value = nil) + using_block = block_given? - results - end - end + inject([[]]) do |results, element| + if (using_block && yield(element)) || (value == element) + results << [] + else + results.last << element end + + results end end end diff --git a/activesupport/lib/active_support/core_ext/array/random_access.rb b/activesupport/lib/active_support/core_ext/array/random_access.rb index 54d17cbf30..343003f6f7 100644 --- a/activesupport/lib/active_support/core_ext/array/random_access.rb +++ b/activesupport/lib/active_support/core_ext/array/random_access.rb @@ -1,12 +1,6 @@ -module ActiveSupport #:nodoc: - module CoreExtensions #:nodoc: - module Array #:nodoc: - module RandomAccess - # Returns a random element from the array. - def rand - self[Kernel.rand(length)] - end - end - end +class Array + # Returns a random element from the array. + def rand + self[Kernel.rand(length)] end end diff --git a/activesupport/lib/active_support/core_ext/array/wrap.rb b/activesupport/lib/active_support/core_ext/array/wrap.rb new file mode 100644 index 0000000000..9d45c2739b --- /dev/null +++ b/activesupport/lib/active_support/core_ext/array/wrap.rb @@ -0,0 +1,18 @@ +class Array + # Wraps the object in an Array unless it's an Array. Converts the + # object to an Array using #to_ary if it implements that. + def self.wrap(object) + case object + when nil + [] + when self + object + else + if object.respond_to?(:to_ary) + object.to_ary + else + [object] + end + end + end +end diff --git a/activesupport/lib/active_support/core_ext/array/wrapper.rb b/activesupport/lib/active_support/core_ext/array/wrapper.rb deleted file mode 100644 index 80b8f05531..0000000000 --- a/activesupport/lib/active_support/core_ext/array/wrapper.rb +++ /dev/null @@ -1,24 +0,0 @@ -module ActiveSupport #:nodoc: - module CoreExtensions #:nodoc: - module Array #:nodoc: - module Wrapper - # Wraps the object in an Array unless it's an Array. Converts the - # object to an Array using #to_ary if it implements that. - def wrap(object) - case object - when nil - [] - when self - object - else - if object.respond_to?(:to_ary) - object.to_ary - else - [object] - end - end - end - end - end - end -end diff --git a/activesupport/lib/active_support/core_ext/base64.rb b/activesupport/lib/active_support/core_ext/base64.rb deleted file mode 100644 index 235e2edf57..0000000000 --- a/activesupport/lib/active_support/core_ext/base64.rb +++ /dev/null @@ -1,4 +0,0 @@ -require 'active_support/base64' -require 'active_support/core_ext/base64/encoding' - -ActiveSupport::Base64.extend ActiveSupport::CoreExtensions::Base64::Encoding diff --git a/activesupport/lib/active_support/core_ext/base64/encoding.rb b/activesupport/lib/active_support/core_ext/base64/encoding.rb deleted file mode 100644 index a9656c138e..0000000000 --- a/activesupport/lib/active_support/core_ext/base64/encoding.rb +++ /dev/null @@ -1,16 +0,0 @@ -module ActiveSupport #:nodoc: - module CoreExtensions #:nodoc: - module Base64 #:nodoc: - module Encoding - # Encodes the value as base64 without the newline breaks. This makes the base64 encoding readily usable as URL parameters - # or memcache keys without further processing. - # - # ActiveSupport::Base64.encode64s("Original unencoded string") - # # => "T3JpZ2luYWwgdW5lbmNvZGVkIHN0cmluZw==" - def encode64s(value) - encode64(value).gsub(/\n/, '') - end - end - end - end -end diff --git a/activesupport/lib/active_support/core_ext/big_decimal.rb b/activesupport/lib/active_support/core_ext/big_decimal.rb new file mode 100644 index 0000000000..8143113cfa --- /dev/null +++ b/activesupport/lib/active_support/core_ext/big_decimal.rb @@ -0,0 +1 @@ +require 'active_support/core_ext/big_decimal/conversions' diff --git a/activesupport/lib/active_support/core_ext/big_decimal/conversions.rb b/activesupport/lib/active_support/core_ext/big_decimal/conversions.rb new file mode 100644 index 0000000000..f7f03f4d95 --- /dev/null +++ b/activesupport/lib/active_support/core_ext/big_decimal/conversions.rb @@ -0,0 +1,27 @@ +require 'bigdecimal' +require 'yaml' + +class BigDecimal + YAML_TAG = 'tag:yaml.org,2002:float' + YAML_MAPPING = { 'Infinity' => '.Inf', '-Infinity' => '-.Inf', 'NaN' => '.NaN' } + + yaml_as YAML_TAG + + # This emits the number without any scientific notation. + # This is better than self.to_f.to_s since it doesn't lose precision. + # + # Note that reconstituting YAML floats to native floats may lose precision. + def to_yaml(opts = {}) + YAML.quick_emit(nil, opts) do |out| + string = to_s + out.scalar(YAML_TAG, YAML_MAPPING[string] || string, :plain) + end + end + + DEFAULT_STRING_FORMAT = 'F' + def to_formatted_s(format = DEFAULT_STRING_FORMAT) + _original_to_s(format) + end + alias_method :_original_to_s, :to_s + alias_method :to_s, :to_formatted_s +end diff --git a/activesupport/lib/active_support/core_ext/bigdecimal.rb b/activesupport/lib/active_support/core_ext/bigdecimal.rb deleted file mode 100644 index d429078c91..0000000000 --- a/activesupport/lib/active_support/core_ext/bigdecimal.rb +++ /dev/null @@ -1,6 +0,0 @@ -require 'bigdecimal' -require 'active_support/core_ext/bigdecimal/conversions' - -class BigDecimal#:nodoc: - include ActiveSupport::CoreExtensions::BigDecimal::Conversions -end diff --git a/activesupport/lib/active_support/core_ext/bigdecimal/conversions.rb b/activesupport/lib/active_support/core_ext/bigdecimal/conversions.rb deleted file mode 100644 index bc9d578f38..0000000000 --- a/activesupport/lib/active_support/core_ext/bigdecimal/conversions.rb +++ /dev/null @@ -1,37 +0,0 @@ -require 'yaml' - -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.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 = DEFAULT_STRING_FORMAT) - _original_to_s(format) - end - - # 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 - end - end -end diff --git a/activesupport/lib/active_support/core_ext/cgi.rb b/activesupport/lib/active_support/core_ext/cgi.rb index db90e5c745..7279a3d4da 100644 --- a/activesupport/lib/active_support/core_ext/cgi.rb +++ b/activesupport/lib/active_support/core_ext/cgi.rb @@ -1,5 +1 @@ require 'active_support/core_ext/cgi/escape_skipping_slashes' - -class CGI #:nodoc: - extend ActiveSupport::CoreExtensions::CGI::EscapeSkippingSlashes -end diff --git a/activesupport/lib/active_support/core_ext/cgi/escape_skipping_slashes.rb b/activesupport/lib/active_support/core_ext/cgi/escape_skipping_slashes.rb index 1edb3771a2..1c3ef05526 100644 --- a/activesupport/lib/active_support/core_ext/cgi/escape_skipping_slashes.rb +++ b/activesupport/lib/active_support/core_ext/cgi/escape_skipping_slashes.rb @@ -1,23 +1,17 @@ -module ActiveSupport #:nodoc: - module CoreExtensions #:nodoc: - module CGI #:nodoc: - module EscapeSkippingSlashes #:nodoc: - if RUBY_VERSION >= '1.9' - def escape_skipping_slashes(str) - str = str.join('/') if str.respond_to? :join - str.gsub(/([^ \/a-zA-Z0-9_.-])/n) do - "%#{$1.unpack('H2' * $1.bytesize).join('%').upcase}" - end.tr(' ', '+') - end - else - def escape_skipping_slashes(str) - str = str.join('/') if str.respond_to? :join - str.gsub(/([^ \/a-zA-Z0-9_.-])/n) do - "%#{$1.unpack('H2').first.upcase}" - end.tr(' ', '+') - end - end - end +class CGI #:nodoc: + if RUBY_VERSION >= '1.9' + def self.escape_skipping_slashes(str) + str = str.join('/') if str.respond_to? :join + str.gsub(/([^ \/a-zA-Z0-9_.-])/n) do + "%#{$1.unpack('H2' * $1.bytesize).join('%').upcase}" + end.tr(' ', '+') + end + else + def self.escape_skipping_slashes(str) + str = str.join('/') if str.respond_to? :join + str.gsub(/([^ \/a-zA-Z0-9_.-])/n) do + "%#{$1.unpack('H2').first.upcase}" + end.tr(' ', '+') end end end diff --git a/activesupport/lib/active_support/core_ext/class/delegating_attributes.rb b/activesupport/lib/active_support/core_ext/class/delegating_attributes.rb index d893818695..da798c67e7 100644 --- a/activesupport/lib/active_support/core_ext/class/delegating_attributes.rb +++ b/activesupport/lib/active_support/core_ext/class/delegating_attributes.rb @@ -1,31 +1,25 @@ -# These class attributes behave something like the class -# inheritable accessors. But instead of copying the hash over at -# the time the subclass is first defined, the accessors simply -# delegate to their superclass unless they have been given a -# specific value. This stops the strange situation where values -# set after class definition don't get applied to subclasses. class Class def superclass_delegating_reader(*names) - class_name_to_stop_searching_on = self.superclass.name.blank? ? "Object" : self.superclass.name + class_name_to_stop_searching_on = superclass.name.blank? ? "Object" : superclass.name names.each do |name| class_eval(<<-EOS, __FILE__, __LINE__ + 1) - def self.#{name} # def self.only_reader - if defined?(@#{name}) # if defined?(@only_reader) - @#{name} # @only_reader - elsif superclass < #{class_name_to_stop_searching_on} && # elsif superclass < Object && - superclass.respond_to?(:#{name}) # superclass.respond_to?(:only_reader) - superclass.#{name} # superclass.only_reader - end # end - end # end - def #{name} # def only_reader - self.class.#{name} # self.class.only_reader - end # end - def self.#{name}? # def self.only_reader? - !!#{name} # !!only_reader - end # end - def #{name}? # def only_reader? - !!#{name} # !!only_reader - end # end + def self.#{name} # def self.only_reader + if defined?(@#{name}) # if defined?(@only_reader) + @#{name} # @only_reader + elsif superclass < #{class_name_to_stop_searching_on} && # elsif superclass < Object && + superclass.respond_to?(:#{name}) # superclass.respond_to?(:only_reader) + superclass.#{name} # superclass.only_reader + end # end + end # end + def #{name} # def only_reader + self.class.#{name} # self.class.only_reader + end # end + def self.#{name}? # def self.only_reader? + !!#{name} # !!only_reader + end # end + def #{name}? # def only_reader? + !!#{name} # !!only_reader + end # end EOS end end @@ -40,6 +34,12 @@ class Class end end + # These class attributes behave something like the class + # inheritable accessors. But instead of copying the hash over at + # the time the subclass is first defined, the accessors simply + # delegate to their superclass unless they have been given a + # specific value. This stops the strange situation where values + # set after class definition don't get applied to subclasses. def superclass_delegating_accessor(*names) superclass_delegating_reader(*names) superclass_delegating_writer(*names) diff --git a/activesupport/lib/active_support/core_ext/date.rb b/activesupport/lib/active_support/core_ext/date.rb index 3f56c560b6..6672129076 100644 --- a/activesupport/lib/active_support/core_ext/date.rb +++ b/activesupport/lib/active_support/core_ext/date.rb @@ -1,10 +1,7 @@ require 'date' -require 'active_support/core_ext/date/behavior' + +require 'active_support/core_ext/date/acts_like' +require 'active_support/core_ext/date/freeze' + require 'active_support/core_ext/date/calculations' require 'active_support/core_ext/date/conversions' - -class Date#:nodoc: - include ActiveSupport::CoreExtensions::Date::Behavior - include ActiveSupport::CoreExtensions::Date::Calculations - include ActiveSupport::CoreExtensions::Date::Conversions -end diff --git a/activesupport/lib/active_support/core_ext/date/acts_like.rb b/activesupport/lib/active_support/core_ext/date/acts_like.rb new file mode 100644 index 0000000000..cd90cee236 --- /dev/null +++ b/activesupport/lib/active_support/core_ext/date/acts_like.rb @@ -0,0 +1,8 @@ +require 'active_support/core_ext/object/acts_like' + +class Date + # Duck-types as a Date-like class. See Object#acts_like?. + def acts_like_date? + true + end +end diff --git a/activesupport/lib/active_support/core_ext/date/behavior.rb b/activesupport/lib/active_support/core_ext/date/behavior.rb deleted file mode 100644 index bd378eb375..0000000000 --- a/activesupport/lib/active_support/core_ext/date/behavior.rb +++ /dev/null @@ -1,42 +0,0 @@ -require 'date' - -module ActiveSupport #:nodoc: - module CoreExtensions #:nodoc: - module Date #:nodoc: - module Behavior - # Enable more predictable duck-typing on Date-like classes. See - # Object#acts_like?. - def acts_like_date? - true - end - - # 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. - # - # 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 - end - end - 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 7f94da015b..59efdbd1b6 100644 --- a/activesupport/lib/active_support/core_ext/date/calculations.rb +++ b/activesupport/lib/active_support/core_ext/date/calculations.rb @@ -1,230 +1,215 @@ -module ActiveSupport #:nodoc: - module CoreExtensions #:nodoc: - module Date #:nodoc: - # Enables the use of time calculations within Date itself - module Calculations - def self.included(base) #:nodoc: - base.extend ClassMethods - - base.instance_eval do - alias_method :plus_without_duration, :+ - alias_method :+, :plus_with_duration - - alias_method :minus_without_duration, :- - alias_method :-, :minus_with_duration - end - end - - module ClassMethods - # Returns a new Date representing the date 1 day ago (i.e. yesterday's date). - 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) - to_time.since(-seconds) - 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 adds the specified number of seconds - def since(seconds) - to_time.since(seconds) - end - alias :in :since - - # Converts Date to a Time (or DateTime if necessary) with the time portion set to the beginning of the day (0:00) - def beginning_of_day - to_time - end - alias :midnight :beginning_of_day - alias :at_midnight :beginning_of_day - alias :at_beginning_of_day :beginning_of_day - - # Converts Date to a Time (or DateTime if necessary) with the time portion set to the end of the day (23:59:59) - def end_of_day - to_time.end_of_day - end - - def plus_with_duration(other) #:nodoc: - if ActiveSupport::Duration === other - other.since(self) - else - plus_without_duration(other) - end - end - - def minus_with_duration(other) #:nodoc: - if ActiveSupport::Duration === other - plus_with_duration(-other) - else - minus_without_duration(other) - end - end - - # 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 - d = d >> options.delete(:years) * 12 if options[:years] - d = d >> options.delete(:months) if options[:months] - d = d + options.delete(:weeks) * 7 if options[:weeks] - d = d + options.delete(:days) if options[:days] - d - end - - # Returns a new Date where one or more of the elements have been changed according to the +options+ parameter. - # - # Examples: - # - # Date.new(2007, 5, 12).change(:day => 1) # => Date.new(2007, 5, 1) - # Date.new(2007, 5, 12).change(:year => 2005, :month => 1) # => Date.new(2005, 1, 12) - def change(options) - ::Date.new( - options[:year] || self.year, - options[:month] || self.month, - 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) - end - - # Returns a new Date/DateTime representing the time a number of specified months in the future - def months_since(months) - advance(:months => months) - end - - # Returns a new Date/DateTime representing the time a number of specified years ago - def years_ago(years) - advance(:years => -years) - end - - # Returns a new Date/DateTime representing the time a number of specified years in the future - def years_since(years) - advance(:years => years) - end - - # Short-hand for years_ago(1) - def last_year - years_ago(1) - end - - # Short-hand for years_since(1) - def next_year - years_since(1) - end - - # Short-hand for months_ago(1) - def last_month - months_ago(1) - end - - # Short-hand for months_since(1) - def next_month - months_since(1) - end - - # Returns a new Date/DateTime representing the "start" of this week (i.e, Monday; DateTime objects will have time set to 0:00) - def beginning_of_week - days_to_monday = self.wday!=0 ? self.wday-1 : 6 - result = self - days_to_monday - self.acts_like?(:time) ? result.midnight : result - end - alias :monday :beginning_of_week - alias :at_beginning_of_week :beginning_of_week - - # Returns a new Date/DateTime representing the end of this week (Sunday, DateTime objects will have time set to 23:59:59) - def end_of_week - days_to_sunday = self.wday!=0 ? 7-self.wday : 0 - result = self + days_to_sunday.days - self.acts_like?(:time) ? result.end_of_day : result - end - alias :at_end_of_week :end_of_week - - # Returns a new Date/DateTime representing the start of the given day in next week (default is Monday). - def next_week(day = :monday) - days_into_week = { :monday => 0, :tuesday => 1, :wednesday => 2, :thursday => 3, :friday => 4, :saturday => 5, :sunday => 6} - result = (self + 7).beginning_of_week + days_into_week[day] - self.acts_like?(:time) ? result.change(:hour => 0) : result - 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 - self.acts_like?(:time) ? change(:day => 1,:hour => 0, :min => 0, :sec => 0) : change(:day => 1) - end - alias :at_beginning_of_month :beginning_of_month - - # Returns a new Date/DateTime representing the end of the month (last day of the month; DateTime objects will have time set to 0:00) - def end_of_month - last_day = ::Time.days_in_month( self.month, self.year ) - self.acts_like?(:time) ? change(:day => last_day, :hour => 23, :min => 59, :sec => 59) : change(:day => last_day) - end - alias :at_end_of_month :end_of_month - - # Returns a new Date/DateTime representing the start of the quarter (1st of january, april, july, october; DateTime objects will have time set to 0:00) - def beginning_of_quarter - beginning_of_month.change(:month => [10, 7, 4, 1].detect { |m| m <= self.month }) - end - alias :at_beginning_of_quarter :beginning_of_quarter - - # Returns a new Date/DateTime representing the end of the quarter (last day of march, june, september, december; DateTime objects will have time set to 23:59:59) - def end_of_quarter - 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 - - # Returns a new Date/DateTime representing the start of the year (1st of january; DateTime objects will have time set to 0:00) - def beginning_of_year - self.acts_like?(:time) ? change(:month => 1, :day => 1, :hour => 0, :min => 0, :sec => 0) : change(:month => 1, :day => 1) - end - alias :at_beginning_of_year :beginning_of_year - - # Returns a new Time representing the end of the year (31st of december; DateTime objects will have time set to 23:59:59) - def end_of_year - self.acts_like?(:time) ? change(:month => 12,:day => 31,:hour => 23, :min => 59, :sec => 59) : change(:month => 12, :day => 31) - end - alias :at_end_of_year :end_of_year - - # Convenience method which returns a new Date/DateTime representing the time 1 day ago - def yesterday - self - 1 - end - - # Convenience method which returns a new Date/DateTime representing the time 1 day since the instance time - def tomorrow - self + 1 - end - end +class Date + class << self + # Returns a new Date representing the date 1 day ago (i.e. yesterday's date). + 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) + to_time.since(-seconds) + 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 adds the specified number of seconds + def since(seconds) + to_time.since(seconds) + end + alias :in :since + + # Converts Date to a Time (or DateTime if necessary) with the time portion set to the beginning of the day (0:00) + def beginning_of_day + to_time + end + alias :midnight :beginning_of_day + alias :at_midnight :beginning_of_day + alias :at_beginning_of_day :beginning_of_day + + # Converts Date to a Time (or DateTime if necessary) with the time portion set to the end of the day (23:59:59) + def end_of_day + to_time.end_of_day + end + + def plus_with_duration(other) #:nodoc: + if ActiveSupport::Duration === other + other.since(self) + else + plus_without_duration(other) + end + end + alias_method :plus_without_duration, :+ + alias_method :+, :plus_with_duration + + def minus_with_duration(other) #:nodoc: + if ActiveSupport::Duration === other + plus_with_duration(-other) + else + minus_without_duration(other) + end + end + alias_method :minus_without_duration, :- + alias_method :-, :minus_with_duration + + # 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 + d = d >> options.delete(:years) * 12 if options[:years] + d = d >> options.delete(:months) if options[:months] + d = d + options.delete(:weeks) * 7 if options[:weeks] + d = d + options.delete(:days) if options[:days] + d + end + + # Returns a new Date where one or more of the elements have been changed according to the +options+ parameter. + # + # Examples: + # + # Date.new(2007, 5, 12).change(:day => 1) # => Date.new(2007, 5, 1) + # Date.new(2007, 5, 12).change(:year => 2005, :month => 1) # => Date.new(2005, 1, 12) + def change(options) + ::Date.new( + options[:year] || self.year, + options[:month] || self.month, + 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) + end + + # Returns a new Date/DateTime representing the time a number of specified months in the future + def months_since(months) + advance(:months => months) + end + + # Returns a new Date/DateTime representing the time a number of specified years ago + def years_ago(years) + advance(:years => -years) + end + + # Returns a new Date/DateTime representing the time a number of specified years in the future + def years_since(years) + advance(:years => years) + end + + # Short-hand for years_ago(1) + def last_year + years_ago(1) + end + + # Short-hand for years_since(1) + def next_year + years_since(1) + end + + # Short-hand for months_ago(1) + def last_month + months_ago(1) + end + + # Short-hand for months_since(1) + def next_month + months_since(1) + end + + # Returns a new Date/DateTime representing the "start" of this week (i.e, Monday; DateTime objects will have time set to 0:00) + def beginning_of_week + days_to_monday = self.wday!=0 ? self.wday-1 : 6 + result = self - days_to_monday + self.acts_like?(:time) ? result.midnight : result + end + alias :monday :beginning_of_week + alias :at_beginning_of_week :beginning_of_week + + # Returns a new Date/DateTime representing the end of this week (Sunday, DateTime objects will have time set to 23:59:59) + def end_of_week + days_to_sunday = self.wday!=0 ? 7-self.wday : 0 + result = self + days_to_sunday.days + self.acts_like?(:time) ? result.end_of_day : result + end + alias :at_end_of_week :end_of_week + + # Returns a new Date/DateTime representing the start of the given day in next week (default is Monday). + def next_week(day = :monday) + days_into_week = { :monday => 0, :tuesday => 1, :wednesday => 2, :thursday => 3, :friday => 4, :saturday => 5, :sunday => 6} + result = (self + 7).beginning_of_week + days_into_week[day] + self.acts_like?(:time) ? result.change(:hour => 0) : result + 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 + self.acts_like?(:time) ? change(:day => 1,:hour => 0, :min => 0, :sec => 0) : change(:day => 1) + end + alias :at_beginning_of_month :beginning_of_month + + # Returns a new Date/DateTime representing the end of the month (last day of the month; DateTime objects will have time set to 0:00) + def end_of_month + last_day = ::Time.days_in_month( self.month, self.year ) + self.acts_like?(:time) ? change(:day => last_day, :hour => 23, :min => 59, :sec => 59) : change(:day => last_day) + end + alias :at_end_of_month :end_of_month + + # Returns a new Date/DateTime representing the start of the quarter (1st of january, april, july, october; DateTime objects will have time set to 0:00) + def beginning_of_quarter + beginning_of_month.change(:month => [10, 7, 4, 1].detect { |m| m <= self.month }) + end + alias :at_beginning_of_quarter :beginning_of_quarter + + # Returns a new Date/DateTime representing the end of the quarter (last day of march, june, september, december; DateTime objects will have time set to 23:59:59) + def end_of_quarter + 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 + + # Returns a new Date/DateTime representing the start of the year (1st of january; DateTime objects will have time set to 0:00) + def beginning_of_year + self.acts_like?(:time) ? change(:month => 1, :day => 1, :hour => 0, :min => 0, :sec => 0) : change(:month => 1, :day => 1) + end + alias :at_beginning_of_year :beginning_of_year + + # Returns a new Time representing the end of the year (31st of december; DateTime objects will have time set to 23:59:59) + def end_of_year + self.acts_like?(:time) ? change(:month => 12,:day => 31,:hour => 23, :min => 59, :sec => 59) : change(:month => 12, :day => 31) + end + alias :at_end_of_year :end_of_year + + # Convenience method which returns a new Date/DateTime representing the time 1 day ago + def yesterday + self - 1 + end + + # Convenience method which returns a new Date/DateTime representing the time 1 day since the instance time + def tomorrow + self + 1 end end diff --git a/activesupport/lib/active_support/core_ext/date/conversions.rb b/activesupport/lib/active_support/core_ext/date/conversions.rb index 8d9f023361..f6c870035b 100644 --- a/activesupport/lib/active_support/core_ext/date/conversions.rb +++ b/activesupport/lib/active_support/core_ext/date/conversions.rb @@ -1,107 +1,97 @@ -module ActiveSupport #:nodoc: - module CoreExtensions #:nodoc: - module Date #:nodoc: - # Converting dates to formatted strings, times, and datetimes. - module Conversions - DATE_FORMATS = { - :short => "%e %b", - :long => "%B %e, %Y", - :db => "%Y-%m-%d", - :number => "%Y%m%d", - :long_ordinal => lambda { |date| date.strftime("%B #{date.day.ordinalize}, %Y") }, # => "April 25th, 2007" - :rfc822 => "%e %b %Y" - } +require 'active_support/inflector' - def self.included(base) #:nodoc: - base.instance_eval do - alias_method :to_default_s, :to_s - alias_method :to_s, :to_formatted_s - alias_method :default_inspect, :inspect - alias_method :inspect, :readable_inspect +class Date + DATE_FORMATS = { + :short => "%e %b", + :long => "%B %e, %Y", + :db => "%Y-%m-%d", + :number => "%Y%m%d", + :long_ordinal => lambda { |date| date.strftime("%B #{ActiveSupport::Inflector.ordinalize(date.day)}, %Y") }, # => "April 25th, 2007" + :rfc822 => "%e %b %Y" + } - # Ruby 1.9 has Date#to_time which converts to localtime only. - remove_method :to_time if base.instance_methods.include?(:to_time) + # Ruby 1.9 has Date#to_time which converts to localtime only. + remove_method :to_time if instance_methods.include?(:to_time) - # Ruby 1.9 has Date#xmlschema which converts to a string without the time component. - remove_method :xmlschema if base.instance_methods.include?(:xmlschema) - end - end + # Ruby 1.9 has Date#xmlschema which converts to a string without the time component. + remove_method :xmlschema if instance_methods.include?(:xmlschema) - # Convert to a formatted string. See DATE_FORMATS for predefined formats. - # - # This method is aliased to <tt>to_s</tt>. - # - # ==== Examples - # date = Date.new(2007, 11, 10) # => Sat, 10 Nov 2007 - # - # date.to_formatted_s(:db) # => "2007-11-10" - # date.to_s(:db) # => "2007-11-10" - # - # date.to_formatted_s(:short) # => "10 Nov" - # date.to_formatted_s(:long) # => "November 10, 2007" - # date.to_formatted_s(:long_ordinal) # => "November 10th, 2007" - # date.to_formatted_s(:rfc822) # => "10 Nov 2007" - # - # == Adding your own time formats to to_formatted_s - # You can add your own formats to the Date::DATE_FORMATS hash. - # Use the format name as the hash key and either a strftime string - # or Proc instance that takes a date argument as the value. - # - # # config/initializers/time_formats.rb - # Date::DATE_FORMATS[:month_and_year] = "%B %Y" - # Date::DATE_FORMATS[:short_ordinal] = lambda { |date| date.strftime("%B #{date.day.ordinalize}") } - def to_formatted_s(format = :default) - if formatter = DATE_FORMATS[format] - if formatter.respond_to?(:call) - formatter.call(self).to_s - else - strftime(formatter) - end - else - to_default_s - end - end + # Convert to a formatted string. See DATE_FORMATS for predefined formats. + # + # This method is aliased to <tt>to_s</tt>. + # + # ==== Examples + # date = Date.new(2007, 11, 10) # => Sat, 10 Nov 2007 + # + # date.to_formatted_s(:db) # => "2007-11-10" + # date.to_s(:db) # => "2007-11-10" + # + # date.to_formatted_s(:short) # => "10 Nov" + # date.to_formatted_s(:long) # => "November 10, 2007" + # date.to_formatted_s(:long_ordinal) # => "November 10th, 2007" + # date.to_formatted_s(:rfc822) # => "10 Nov 2007" + # + # == Adding your own time formats to to_formatted_s + # You can add your own formats to the Date::DATE_FORMATS hash. + # Use the format name as the hash key and either a strftime string + # or Proc instance that takes a date argument as the value. + # + # # config/initializers/time_formats.rb + # Date::DATE_FORMATS[:month_and_year] = "%B %Y" + # Date::DATE_FORMATS[:short_ordinal] = lambda { |date| date.strftime("%B #{date.day.ordinalize}") } + def to_formatted_s(format = :default) + if formatter = DATE_FORMATS[format] + if formatter.respond_to?(:call) + formatter.call(self).to_s + else + strftime(formatter) + end + else + to_default_s + end + end + alias_method :to_default_s, :to_s + alias_method :to_s, :to_formatted_s - # Overrides the default inspect method with a human readable one, e.g., "Mon, 21 Feb 2005" - def readable_inspect - strftime("%a, %d %b %Y") - end + # Overrides the default inspect method with a human readable one, e.g., "Mon, 21 Feb 2005" + def readable_inspect + strftime("%a, %d %b %Y") + end + alias_method :default_inspect, :inspect + alias_method :inspect, :readable_inspect - # A method to keep Time, Date and DateTime instances interchangeable on conversions. - # In this case, it simply returns +self+. - def to_date - self - end if RUBY_VERSION < '1.9' + # A method to keep Time, Date and DateTime instances interchangeable on conversions. + # In this case, it simply returns +self+. + def to_date + self + end if RUBY_VERSION < '1.9' - # Converts a Date instance to a Time, where the time is set to the beginning of the day. - # The timezone can be either :local or :utc (default :local). - # - # ==== Examples - # date = Date.new(2007, 11, 10) # => Sat, 10 Nov 2007 - # - # date.to_time # => Sat Nov 10 00:00:00 0800 2007 - # date.to_time(:local) # => Sat Nov 10 00:00:00 0800 2007 - # - # date.to_time(:utc) # => Sat Nov 10 00:00:00 UTC 2007 - def to_time(form = :local) - ::Time.send("#{form}_time", year, month, day) - end + # Converts a Date instance to a Time, where the time is set to the beginning of the day. + # The timezone can be either :local or :utc (default :local). + # + # ==== Examples + # date = Date.new(2007, 11, 10) # => Sat, 10 Nov 2007 + # + # date.to_time # => Sat Nov 10 00:00:00 0800 2007 + # date.to_time(:local) # => Sat Nov 10 00:00:00 0800 2007 + # + # date.to_time(:utc) # => Sat Nov 10 00:00:00 UTC 2007 + def to_time(form = :local) + ::Time.send("#{form}_time", year, month, day) + end - # Converts a Date instance to a DateTime, where the time is set to the beginning of the day - # and UTC offset is set to 0. - # - # ==== Examples - # date = Date.new(2007, 11, 10) # => Sat, 10 Nov 2007 - # - # date.to_datetime # => Sat, 10 Nov 2007 00:00:00 0000 - def to_datetime - ::DateTime.civil(year, month, day, 0, 0, 0, 0) - end if RUBY_VERSION < '1.9' + # Converts a Date instance to a DateTime, where the time is set to the beginning of the day + # and UTC offset is set to 0. + # + # ==== Examples + # date = Date.new(2007, 11, 10) # => Sat, 10 Nov 2007 + # + # date.to_datetime # => Sat, 10 Nov 2007 00:00:00 0000 + def to_datetime + ::DateTime.civil(year, month, day, 0, 0, 0, 0) + end if RUBY_VERSION < '1.9' - def xmlschema - to_time.xmlschema - end - end - end + def xmlschema + to_time.xmlschema end end diff --git a/activesupport/lib/active_support/core_ext/date/freeze.rb b/activesupport/lib/active_support/core_ext/date/freeze.rb new file mode 100644 index 0000000000..4edd715ba2 --- /dev/null +++ b/activesupport/lib/active_support/core_ext/date/freeze.rb @@ -0,0 +1,31 @@ +# 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. +# +# Ruby 1.9 uses a preinitialized instance variable so it's unaffected. +# This hack is as close as we can get to feature detection: +if RUBY_VERSION < '1.9' + require 'date' + begin + ::Date.today.freeze.jd + rescue => frozen_object_error + if frozen_object_error.message =~ /frozen/ + class Date #:nodoc: + def freeze + 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 + end + end +end diff --git a/activesupport/lib/active_support/core_ext/date_time.rb b/activesupport/lib/active_support/core_ext/date_time.rb index 66a93eac43..83a11da1c7 100644 --- a/activesupport/lib/active_support/core_ext/date_time.rb +++ b/activesupport/lib/active_support/core_ext/date_time.rb @@ -1,12 +1,6 @@ require 'date' -require 'active_support/core_ext/time/behavior' -require 'active_support/core_ext/time/zones' + +require 'active_support/core_ext/date_time/acts_like' require 'active_support/core_ext/date_time/calculations' require 'active_support/core_ext/date_time/conversions' - -class DateTime - include ActiveSupport::CoreExtensions::Time::Behavior - include ActiveSupport::CoreExtensions::Time::Zones - include ActiveSupport::CoreExtensions::DateTime::Calculations - include ActiveSupport::CoreExtensions::DateTime::Conversions -end +require 'active_support/core_ext/date_time/zones' diff --git a/activesupport/lib/active_support/core_ext/date_time/acts_like.rb b/activesupport/lib/active_support/core_ext/date_time/acts_like.rb new file mode 100644 index 0000000000..c79745c5aa --- /dev/null +++ b/activesupport/lib/active_support/core_ext/date_time/acts_like.rb @@ -0,0 +1,13 @@ +require 'active_support/core_ext/object/acts_like' + +class DateTime + # Duck-types as a Date-like class. See Object#acts_like?. + def acts_like_date? + true + end + + # Duck-types as a Time-like class. See Object#acts_like?. + def acts_like_time? + true + end +end 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 0099431e9d..41cf020f94 100644 --- a/activesupport/lib/active_support/core_ext/date_time/calculations.rb +++ b/activesupport/lib/active_support/core_ext/date_time/calculations.rb @@ -1,126 +1,112 @@ require 'rational' -module ActiveSupport #:nodoc: - module CoreExtensions #:nodoc: - module DateTime #:nodoc: - # Enables the use of time calculations within DateTime itself - module Calculations - def self.included(base) #:nodoc: - base.extend ClassMethods - - base.class_eval do - alias_method :compare_without_coercion, :<=> - alias_method :<=>, :compare_with_coercion - end - end - - module ClassMethods - # DateTimes aren't aware of DST rules, so use a consistent non-DST offset when creating a DateTime with an offset in the local zone - def local_offset - ::Time.local(2007).utc_offset.to_r / 86400 - end +class DateTime + class << self + # DateTimes aren't aware of DST rules, so use a consistent non-DST offset when creating a DateTime with an offset in the local zone + 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 + 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 past + def past? + self < ::DateTime.current + end - # Tells whether the DateTime object's datetime lies in the future - def future? - 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 - def seconds_since_midnight - self.sec + (self.min * 60) + (self.hour * 3600) - end + # Seconds since midnight: DateTime.now.seconds_since_midnight + def seconds_since_midnight + sec + (min * 60) + (hour * 3600) + end - # Returns a new DateTime where one or more of the elements have been changed according to the +options+ parameter. The time options - # (hour, minute, sec) reset cascadingly, so if only the hour is passed, then minute and sec is set to 0. If the hour and - # minute is passed, then sec is set to 0. - def change(options) - ::DateTime.civil( - options[:year] || self.year, - options[:month] || self.month, - options[:day] || self.day, - options[:hour] || self.hour, - options[:min] || (options[:hour] ? 0 : self.min), - options[:sec] || ((options[:hour] || options[:min]) ? 0 : self.sec), - options[:offset] || self.offset, - options[:start] || self.start - ) - end + # Returns a new DateTime where one or more of the elements have been changed according to the +options+ parameter. The time options + # (hour, minute, sec) reset cascadingly, so if only the hour is passed, then minute and sec is set to 0. If the hour and + # minute is passed, then sec is set to 0. + def change(options) + ::DateTime.civil( + options[:year] || year, + options[:month] || month, + options[:day] || day, + options[:hour] || hour, + options[:min] || (options[:hour] ? 0 : min), + options[:sec] || ((options[:hour] || options[:min]) ? 0 : sec), + options[:offset] || offset, + options[:start] || start + ) + end - # Uses Date to provide precise Time 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>, <tt>:hours</tt>, - # <tt>:minutes</tt>, <tt>:seconds</tt>. - def advance(options) - d = to_date.advance(options) - datetime_advanced_by_date = change(:year => d.year, :month => d.month, :day => d.day) - seconds_to_advance = (options[:seconds] || 0) + (options[:minutes] || 0) * 60 + (options[:hours] || 0) * 3600 - seconds_to_advance == 0 ? datetime_advanced_by_date : datetime_advanced_by_date.since(seconds_to_advance) - end + # Uses Date to provide precise Time 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>, <tt>:hours</tt>, + # <tt>:minutes</tt>, <tt>:seconds</tt>. + def advance(options) + d = to_date.advance(options) + datetime_advanced_by_date = change(:year => d.year, :month => d.month, :day => d.day) + seconds_to_advance = (options[:seconds] || 0) + (options[:minutes] || 0) * 60 + (options[:hours] || 0) * 3600 + seconds_to_advance == 0 ? datetime_advanced_by_date : datetime_advanced_by_date.since(seconds_to_advance) + end - # Returns a new DateTime representing the time a number of seconds ago - # Do not use this method in combination with x.months, use months_ago instead! - def ago(seconds) - self.since(-seconds) - end + # Returns a new DateTime representing the time a number of seconds ago + # Do not use this method in combination with x.months, use months_ago instead! + def ago(seconds) + since(-seconds) + end - # Returns a new DateTime representing the time a number of seconds since the instance time - # Do not use this method in combination with x.months, use months_since instead! - def since(seconds) - self + Rational(seconds.round, 86400) - end - alias :in :since + # Returns a new DateTime representing the time a number of seconds since the instance time + # Do not use this method in combination with x.months, use months_since instead! + def since(seconds) + self + Rational(seconds.round, 86400) + end + alias :in :since - # Returns a new DateTime representing the start of the day (0:00) - def beginning_of_day - change(:hour => 0) - end - alias :midnight :beginning_of_day - alias :at_midnight :beginning_of_day - alias :at_beginning_of_day :beginning_of_day + # Returns a new DateTime representing the start of the day (0:00) + def beginning_of_day + change(:hour => 0) + end + alias :midnight :beginning_of_day + alias :at_midnight :beginning_of_day + alias :at_beginning_of_day :beginning_of_day - # Returns a new DateTime representing the end of the day (23:59:59) - def end_of_day - change(:hour => 23, :min => 59, :sec => 59) - end + # Returns a new DateTime representing the end of the day (23:59:59) + 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: - # - # DateTime.civil(2005, 2, 21, 10, 11, 12, Rational(-6, 24)) # => Mon, 21 Feb 2005 10:11:12 -0600 - # DateTime.civil(2005, 2, 21, 10, 11, 12, Rational(-6, 24)).utc # => Mon, 21 Feb 2005 16:11:12 +0000 - def utc - new_offset(0) - end - alias_method :getutc, :utc + # Adjusts DateTime to UTC by adding its offset value; offset is set to 0 + # + # Example: + # + # DateTime.civil(2005, 2, 21, 10, 11, 12, Rational(-6, 24)) # => Mon, 21 Feb 2005 10:11:12 -0600 + # DateTime.civil(2005, 2, 21, 10, 11, 12, Rational(-6, 24)).utc # => Mon, 21 Feb 2005 16:11:12 +0000 + def utc + new_offset(0) + end + alias_method :getutc, :utc - # Returns true if offset == 0 - def utc? - offset == 0 - end + # Returns true if offset == 0 + def utc? + offset == 0 + end - # Returns the offset value in seconds - def utc_offset - (offset * 86400).to_i - 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) - other = other.to_datetime unless other.acts_like?(:date) - compare_without_coercion(other) - end - end - end + # Layers additional behavior on DateTime#<=> so that Time and ActiveSupport::TimeWithZone instances can be compared with a DateTime + def compare_with_coercion(other) + other = other.comparable_time if other.respond_to?(:comparable_time) + other = other.to_datetime unless other.acts_like?(:date) + compare_without_coercion(other) end + alias_method :compare_without_coercion, :<=> + alias_method :<=>, :compare_with_coercion end diff --git a/activesupport/lib/active_support/core_ext/date_time/conversions.rb b/activesupport/lib/active_support/core_ext/date_time/conversions.rb index 7c948267b3..ddfa8d610d 100644 --- a/activesupport/lib/active_support/core_ext/date_time/conversions.rb +++ b/activesupport/lib/active_support/core_ext/date_time/conversions.rb @@ -1,96 +1,84 @@ -module ActiveSupport #:nodoc: - module CoreExtensions #:nodoc: - module DateTime #:nodoc: - # Converting datetimes to formatted strings, dates, and times. - module Conversions - def self.append_features(base) #:nodoc: - base.class_eval do - alias_method :default_inspect, :inspect - alias_method :to_default_s, :to_s unless (instance_methods(false) & [:to_s, 'to_s']).empty? +require 'active_support/inflector' - # Ruby 1.9 has DateTime#to_time which internally relies on Time. We define our own #to_time which allows - # DateTimes outside the range of what can be created with Time. - remove_method :to_time if instance_methods.include?(:to_time) - end +class DateTime + # Ruby 1.9 has DateTime#to_time which internally relies on Time. We define our own #to_time which allows + # DateTimes outside the range of what can be created with Time. + remove_method :to_time if instance_methods.include?(:to_time) - super - - base.class_eval do - alias_method :to_s, :to_formatted_s - alias_method :inspect, :readable_inspect - end - end - - # Convert to a formatted string. See Time::DATE_FORMATS for predefined formats. - # - # This method is aliased to <tt>to_s</tt>. - # - # === Examples - # datetime = DateTime.civil(2007, 12, 4, 0, 0, 0, 0) # => Tue, 04 Dec 2007 00:00:00 +0000 - # - # datetime.to_formatted_s(:db) # => "2007-12-04 00:00:00" - # datetime.to_s(:db) # => "2007-12-04 00:00:00" - # datetime.to_s(:number) # => "20071204000000" - # datetime.to_formatted_s(:short) # => "04 Dec 00:00" - # datetime.to_formatted_s(:long) # => "December 04, 2007 00:00" - # datetime.to_formatted_s(:long_ordinal) # => "December 4th, 2007 00:00" - # datetime.to_formatted_s(:rfc822) # => "Tue, 04 Dec 2007 00:00:00 +0000" - # - # == Adding your own datetime formats to to_formatted_s - # DateTime formats are shared with Time. You can add your own to the - # Time::DATE_FORMATS hash. Use the format name as the hash key and - # either a strftime string or Proc instance that takes a time or - # datetime argument as the value. - # - # # config/initializers/time_formats.rb - # Time::DATE_FORMATS[:month_and_year] = "%B %Y" - # Time::DATE_FORMATS[:short_ordinal] = lambda { |time| time.strftime("%B #{time.day.ordinalize}") } - def to_formatted_s(format = :default) - return to_default_s unless formatter = ::Time::DATE_FORMATS[format] - formatter.respond_to?(:call) ? formatter.call(self).to_s : strftime(formatter) - end + # Convert to a formatted string. See Time::DATE_FORMATS for predefined formats. + # + # This method is aliased to <tt>to_s</tt>. + # + # === Examples + # datetime = DateTime.civil(2007, 12, 4, 0, 0, 0, 0) # => Tue, 04 Dec 2007 00:00:00 +0000 + # + # datetime.to_formatted_s(:db) # => "2007-12-04 00:00:00" + # datetime.to_s(:db) # => "2007-12-04 00:00:00" + # datetime.to_s(:number) # => "20071204000000" + # datetime.to_formatted_s(:short) # => "04 Dec 00:00" + # datetime.to_formatted_s(:long) # => "December 04, 2007 00:00" + # datetime.to_formatted_s(:long_ordinal) # => "December 4th, 2007 00:00" + # datetime.to_formatted_s(:rfc822) # => "Tue, 04 Dec 2007 00:00:00 +0000" + # + # == Adding your own datetime formats to to_formatted_s + # DateTime formats are shared with Time. You can add your own to the + # Time::DATE_FORMATS hash. Use the format name as the hash key and + # either a strftime string or Proc instance that takes a time or + # datetime argument as the value. + # + # # config/initializers/time_formats.rb + # Time::DATE_FORMATS[:month_and_year] = "%B %Y" + # Time::DATE_FORMATS[:short_ordinal] = lambda { |time| time.strftime("%B #{time.day.ordinalize}") } + def to_formatted_s(format = :default) + if formatter = ::Time::DATE_FORMATS[format] + formatter.respond_to?(:call) ? formatter.call(self).to_s : strftime(formatter) + else + to_default_s + end + end + alias_method :to_default_s, :to_s unless (instance_methods(false) & [:to_s, 'to_s']).empty? + alias_method :to_s, :to_formatted_s - # Returns the +utc_offset+ as an +HH:MM formatted string. Examples: - # - # datetime = DateTime.civil(2000, 1, 1, 0, 0, 0, Rational(-6, 24)) - # datetime.formatted_offset # => "-06:00" - # datetime.formatted_offset(false) # => "-0600" - def formatted_offset(colon = true, alternate_utc_string = nil) - utc? && alternate_utc_string || utc_offset.to_utc_offset_s(colon) - end - - # Overrides the default inspect method with a human readable one, e.g., "Mon, 21 Feb 2005 14:30:00 +0000" - def readable_inspect - to_s(:rfc822) - end + # Returns the +utc_offset+ as an +HH:MM formatted string. Examples: + # + # datetime = DateTime.civil(2000, 1, 1, 0, 0, 0, Rational(-6, 24)) + # datetime.formatted_offset # => "-06:00" + # datetime.formatted_offset(false) # => "-0600" + def formatted_offset(colon = true, alternate_utc_string = nil) + utc? && alternate_utc_string || ActiveSupport::TimeZone.seconds_to_utc_offset(utc_offset, colon) + end + + # Overrides the default inspect method with a human readable one, e.g., "Mon, 21 Feb 2005 14:30:00 +0000" + def readable_inspect + to_s(:rfc822) + end + alias_method :default_inspect, :inspect + alias_method :inspect, :readable_inspect - # Converts self to a Ruby Date object; time portion is discarded - def to_date - ::Date.new(year, month, day) - end + # Converts self to a Ruby Date object; time portion is discarded + def to_date + ::Date.new(year, month, day) + end - # Attempts to convert self to a Ruby Time object; returns self if out of range of Ruby Time class - # If self has an offset other than 0, self will just be returned unaltered, since there's no clean way to map it to a Time - def to_time - self.offset == 0 ? ::Time.utc_time(year, month, day, hour, min, sec) : self - end + # Attempts to convert self to a Ruby Time object; returns self if out of range of Ruby Time class + # If self has an offset other than 0, self will just be returned unaltered, since there's no clean way to map it to a Time + def to_time + self.offset == 0 ? ::Time.utc_time(year, month, day, hour, min, sec) : self + end - # To be able to keep Times, Dates and DateTimes interchangeable on conversions - def to_datetime - self - end + # To be able to keep Times, Dates and DateTimes interchangeable on conversions + def to_datetime + self + end - # Converts datetime to an appropriate format for use in XML - def xmlschema - strftime("%Y-%m-%dT%H:%M:%S%Z") - end if RUBY_VERSION < '1.9' - - # Converts self to a floating-point number of seconds since the Unix epoch - def to_f - days_since_unix_epoch = self - ::DateTime.civil(1970) - (days_since_unix_epoch * 86_400).to_f - end - end - end + # Converts datetime to an appropriate format for use in XML + def xmlschema + strftime("%Y-%m-%dT%H:%M:%S%Z") + end if RUBY_VERSION < '1.9' + + # Converts self to a floating-point number of seconds since the Unix epoch + def to_f + days_since_unix_epoch = self - ::DateTime.civil(1970) + (days_since_unix_epoch * 86_400).to_f end end diff --git a/activesupport/lib/active_support/core_ext/date_time/zones.rb b/activesupport/lib/active_support/core_ext/date_time/zones.rb new file mode 100644 index 0000000000..98565e6750 --- /dev/null +++ b/activesupport/lib/active_support/core_ext/date_time/zones.rb @@ -0,0 +1,17 @@ +class DateTime + # Returns the simultaneous time in <tt>Time.zone</tt>. + # + # Time.zone = 'Hawaii' # => 'Hawaii' + # DateTime.new(2000).in_time_zone # => Fri, 31 Dec 1999 14:00:00 HST -10:00 + # + # This method is similar to Time#localtime, except that it uses <tt>Time.zone</tt> as the local zone + # instead of the operating system's time zone. + # + # You can also pass in a TimeZone instance or string that identifies a TimeZone as an argument, + # and the conversion will be based on that zone instead of <tt>Time.zone</tt>. + # + # DateTime.new(2000).in_time_zone('Alaska') # => Fri, 31 Dec 1999 15:00:00 AKST -09:00 + def in_time_zone(zone = ::Time.zone) + ActiveSupport::TimeWithZone.new(utc? ? self : getutc, ::Time.__send__(:get_zone, zone)) + end +end diff --git a/activesupport/lib/active_support/core_ext/enumerable.rb b/activesupport/lib/active_support/core_ext/enumerable.rb index a7eaccfed7..8309b617ae 100644 --- a/activesupport/lib/active_support/core_ext/enumerable.rb +++ b/activesupport/lib/active_support/core_ext/enumerable.rb @@ -77,11 +77,10 @@ module Enumerable # (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 + each do |element| + block.call(element, memo) end + memo end unless [].respond_to?(:each_with_object) # Convert an enumerable to a hash. Examples: diff --git a/activesupport/lib/active_support/core_ext/exception.rb b/activesupport/lib/active_support/core_ext/exception.rb index cde0df4153..b594fbae8e 100644 --- a/activesupport/lib/active_support/core_ext/exception.rb +++ b/activesupport/lib/active_support/core_ext/exception.rb @@ -1,15 +1,19 @@ module ActiveSupport - if RUBY_VERSION >= '1.9' - FrozenObjectError = RuntimeError - else - FrozenObjectError = TypeError - end + FrozenObjectError = RUBY_VERSION < '1.9' ? TypeError : RuntimeError end # TODO: Turn all this into using the BacktraceCleaner. class Exception # :nodoc: + # Clean the paths contained in the message. + def self.clean_paths(string) + require 'pathname' unless defined? Pathname + string.gsub(%r{[\w. ]+(/[\w. ]+)+(\.rb)?(\b|$)}) do |path| + Pathname.new(path).cleanpath + end + end + def clean_message - Pathname.clean_within message + Exception.clean_paths(message) end TraceSubstitutions = [] @@ -18,9 +22,10 @@ class Exception # :nodoc: def clean_backtrace backtrace.collect do |line| - Pathname.clean_within(TraceSubstitutions.inject(line) do |result, (regexp, sub)| + substituted = TraceSubstitutions.inject(line) do |result, (regexp, sub)| result.gsub regexp, sub - end) + end + Exception.clean_paths(substituted) end end diff --git a/activesupport/lib/active_support/core_ext/file.rb b/activesupport/lib/active_support/core_ext/file.rb index e03f8ac44e..dc24afbe7f 100644 --- a/activesupport/lib/active_support/core_ext/file.rb +++ b/activesupport/lib/active_support/core_ext/file.rb @@ -1,5 +1 @@ require 'active_support/core_ext/file/atomic' - -class File #:nodoc: - extend ActiveSupport::CoreExtensions::File::Atomic -end diff --git a/activesupport/lib/active_support/core_ext/file/atomic.rb b/activesupport/lib/active_support/core_ext/file/atomic.rb index 8cc5654a4b..249fb1362d 100644 --- a/activesupport/lib/active_support/core_ext/file/atomic.rb +++ b/activesupport/lib/active_support/core_ext/file/atomic.rb @@ -1,46 +1,40 @@ -module ActiveSupport #:nodoc: - module CoreExtensions #:nodoc: - module File #:nodoc: - module Atomic - # Write to a file atomically. Useful for situations where you don't - # want other processes or threads to see half-written files. - # - # File.atomic_write("important.file") do |file| - # file.write("hello") - # end - # - # If your temp directory is not on the same filesystem as the file you're - # trying to write, you can provide a different temporary directory. - # - # File.atomic_write("/data/something.important", "/data/tmp") do |f| - # file.write("hello") - # end - def atomic_write(file_name, temp_dir = Dir.tmpdir) - require 'tempfile' unless defined?(Tempfile) +class File + # Write to a file atomically. Useful for situations where you don't + # want other processes or threads to see half-written files. + # + # File.atomic_write("important.file") do |file| + # file.write("hello") + # end + # + # If your temp directory is not on the same filesystem as the file you're + # trying to write, you can provide a different temporary directory. + # + # File.atomic_write("/data/something.important", "/data/tmp") do |f| + # file.write("hello") + # end + def self.atomic_write(file_name, temp_dir = Dir.tmpdir) + require 'tempfile' unless defined?(Tempfile) - temp_file = Tempfile.new(basename(file_name), temp_dir) - yield temp_file - temp_file.close + temp_file = Tempfile.new(basename(file_name), temp_dir) + yield temp_file + temp_file.close - begin - # Get original file permissions - old_stat = stat(file_name) - rescue Errno::ENOENT - # No old permissions, write a temp file to determine the defaults - check_name = join(dirname(file_name), ".permissions_check.#{Thread.current.object_id}.#{Process.pid}.#{rand(1000000)}") - open(check_name, "w") { } - old_stat = stat(check_name) - unlink(check_name) - end + begin + # Get original file permissions + old_stat = stat(file_name) + rescue Errno::ENOENT + # No old permissions, write a temp file to determine the defaults + check_name = join(dirname(file_name), ".permissions_check.#{Thread.current.object_id}.#{Process.pid}.#{rand(1000000)}") + open(check_name, "w") { } + old_stat = stat(check_name) + unlink(check_name) + end - # Overwrite original file with temp file - rename(temp_file.path, file_name) + # Overwrite original file with temp file + rename(temp_file.path, file_name) - # Set correct permissions on new file - chown(old_stat.uid, old_stat.gid, file_name) - chmod(old_stat.mode, file_name) - end - end - end + # Set correct permissions on new file + chown(old_stat.uid, old_stat.gid, file_name) + chmod(old_stat.mode, file_name) end end diff --git a/activesupport/lib/active_support/core_ext/float.rb b/activesupport/lib/active_support/core_ext/float.rb index af166aa610..7570471b95 100644 --- a/activesupport/lib/active_support/core_ext/float.rb +++ b/activesupport/lib/active_support/core_ext/float.rb @@ -1,7 +1 @@ require 'active_support/core_ext/float/rounding' -require 'active_support/core_ext/float/time' - -class Float #:nodoc: - include ActiveSupport::CoreExtensions::Float::Rounding - include ActiveSupport::CoreExtensions::Float::Time -end diff --git a/activesupport/lib/active_support/core_ext/float/rounding.rb b/activesupport/lib/active_support/core_ext/float/rounding.rb index 062d466838..0b1ae4be7e 100644 --- a/activesupport/lib/active_support/core_ext/float/rounding.rb +++ b/activesupport/lib/active_support/core_ext/float/rounding.rb @@ -1,24 +1,18 @@ -module ActiveSupport #:nodoc: - module CoreExtensions #:nodoc: - module Float #:nodoc: - module Rounding - def self.included(base) #:nodoc: - base.class_eval do - alias_method :round_without_precision, :round - alias_method :round, :round_with_precision - end - end +class Float + remove_method :round - # Rounds the float with the specified precision. - # - # x = 1.337 - # x.round # => 1 - # x.round(1) # => 1.3 - # x.round(2) # => 1.34 - def round_with_precision(precision = nil) - precision.nil? ? round_without_precision : (self * (10 ** precision)).round / (10 ** precision).to_f - end - end + # Rounds the float with the specified precision. + # + # x = 1.337 + # x.round # => 1 + # x.round(1) # => 1.3 + # x.round(2) # => 1.34 + def round(precision = nil) + if precision + magnitude = 10.0 ** precision + (self * magnitude).round / magnitude + else + super() end end end diff --git a/activesupport/lib/active_support/core_ext/float/time.rb b/activesupport/lib/active_support/core_ext/float/time.rb deleted file mode 100644 index 13f2e0ddca..0000000000 --- a/activesupport/lib/active_support/core_ext/float/time.rb +++ /dev/null @@ -1,27 +0,0 @@ -module ActiveSupport #:nodoc: - module CoreExtensions #:nodoc: - module Float #:nodoc: - module Time - # Deprication helper methods not available as core_ext is loaded first. - def years - ::ActiveSupport::Deprecation.warn(self.class.deprecated_method_warning(:years, "Fractional years are not respected. Convert value to integer before calling #years."), caller) - years_without_deprecation - end - def months - ::ActiveSupport::Deprecation.warn(self.class.deprecated_method_warning(:months, "Fractional months are not respected. Convert value to integer before calling #months."), caller) - months_without_deprecation - end - - def months_without_deprecation - ActiveSupport::Duration.new(self * 30.days, [[:months, self]]) - end - alias :month :months - - def years_without_deprecation - ActiveSupport::Duration.new(self * 365.25.days, [[:years, self]]) - end - alias :year :years - end - end - end -end
\ No newline at end of file diff --git a/activesupport/lib/active_support/core_ext/hash.rb b/activesupport/lib/active_support/core_ext/hash.rb index a6065ab48e..501483498d 100644 --- a/activesupport/lib/active_support/core_ext/hash.rb +++ b/activesupport/lib/active_support/core_ext/hash.rb @@ -1,14 +1,8 @@ -%w(keys indifferent_access deep_merge reverse_merge conversions diff slice except).each do |ext| - require "active_support/core_ext/hash/#{ext}" -end - -class Hash #:nodoc: - include ActiveSupport::CoreExtensions::Hash::Keys - include ActiveSupport::CoreExtensions::Hash::IndifferentAccess - include ActiveSupport::CoreExtensions::Hash::DeepMerge - include ActiveSupport::CoreExtensions::Hash::ReverseMerge - include ActiveSupport::CoreExtensions::Hash::Conversions - include ActiveSupport::CoreExtensions::Hash::Diff - include ActiveSupport::CoreExtensions::Hash::Slice - include ActiveSupport::CoreExtensions::Hash::Except -end +require 'active_support/core_ext/hash/conversions' +require 'active_support/core_ext/hash/deep_merge' +require 'active_support/core_ext/hash/diff' +require 'active_support/core_ext/hash/except' +require 'active_support/core_ext/hash/indifferent_access' +require 'active_support/core_ext/hash/keys' +require 'active_support/core_ext/hash/reverse_merge' +require 'active_support/core_ext/hash/slice' diff --git a/activesupport/lib/active_support/core_ext/hash/conversions.rb b/activesupport/lib/active_support/core_ext/hash/conversions.rb index f8a5e70eea..fa171720f9 100644 --- a/activesupport/lib/active_support/core_ext/hash/conversions.rb +++ b/activesupport/lib/active_support/core_ext/hash/conversions.rb @@ -1,236 +1,228 @@ require 'date' +require 'active_support/core_ext/object/conversions' +require 'active_support/core_ext/array/conversions' -module ActiveSupport #:nodoc: - module CoreExtensions #:nodoc: - module Hash #:nodoc: - module Conversions - # This module exists to decorate files deserialized using Hash.from_xml with - # the <tt>original_filename</tt> and <tt>content_type</tt> methods. - module FileLike #:nodoc: - attr_writer :original_filename, :content_type - - def original_filename - @original_filename || 'untitled' - end - - def content_type - @content_type || 'application/octet-stream' - end - end +class Hash + # This module exists to decorate files deserialized using Hash.from_xml with + # the <tt>original_filename</tt> and <tt>content_type</tt> methods. + module FileLike #:nodoc: + attr_writer :original_filename, :content_type - XML_TYPE_NAMES = { - "Symbol" => "symbol", - "Fixnum" => "integer", - "Bignum" => "integer", - "BigDecimal" => "decimal", - "Float" => "float", - "TrueClass" => "boolean", - "FalseClass" => "boolean", - "Date" => "date", - "DateTime" => "datetime", - "Time" => "datetime" - } unless defined?(XML_TYPE_NAMES) - - XML_FORMATTING = { - "symbol" => Proc.new { |symbol| symbol.to_s }, - "date" => Proc.new { |date| date.to_s(:db) }, - "datetime" => Proc.new { |time| time.xmlschema }, - "binary" => Proc.new { |binary| ActiveSupport::Base64.encode64(binary) }, - "yaml" => Proc.new { |yaml| yaml.to_yaml } - } unless defined?(XML_FORMATTING) - - # TODO: use Time.xmlschema instead of Time.parse; - # use regexp instead of Date.parse - unless defined?(XML_PARSING) - XML_PARSING = { - "symbol" => Proc.new { |symbol| symbol.to_sym }, - "date" => Proc.new { |date| ::Date.parse(date) }, - "datetime" => Proc.new { |time| ::Time.parse(time).utc rescue ::DateTime.parse(time).utc }, - "integer" => Proc.new { |integer| integer.to_i }, - "float" => Proc.new { |float| float.to_f }, - "decimal" => Proc.new { |number| BigDecimal(number) }, - "boolean" => Proc.new { |boolean| %w(1 true).include?(boolean.strip) }, - "string" => Proc.new { |string| string.to_s }, - "yaml" => Proc.new { |yaml| YAML::load(yaml) rescue yaml }, - "base64Binary" => Proc.new { |bin| ActiveSupport::Base64.decode64(bin) }, - "file" => Proc.new do |file, entity| - f = StringIO.new(ActiveSupport::Base64.decode64(file)) - f.extend(FileLike) - f.original_filename = entity['name'] - f.content_type = entity['content_type'] - f - end - } + def original_filename + @original_filename || 'untitled' + end - XML_PARSING.update( - "double" => XML_PARSING["float"], - "dateTime" => XML_PARSING["datetime"] - ) - end + def content_type + @content_type || 'application/octet-stream' + end + end - def self.included(klass) - klass.extend(ClassMethods) - end + XML_TYPE_NAMES = { + "Symbol" => "symbol", + "Fixnum" => "integer", + "Bignum" => "integer", + "BigDecimal" => "decimal", + "Float" => "float", + "TrueClass" => "boolean", + "FalseClass" => "boolean", + "Date" => "date", + "DateTime" => "datetime", + "Time" => "datetime" + } unless defined?(XML_TYPE_NAMES) + + XML_FORMATTING = { + "symbol" => Proc.new { |symbol| symbol.to_s }, + "date" => Proc.new { |date| date.to_s(:db) }, + "datetime" => Proc.new { |time| time.xmlschema }, + "binary" => Proc.new { |binary| ActiveSupport::Base64.encode64(binary) }, + "yaml" => Proc.new { |yaml| yaml.to_yaml } + } unless defined?(XML_FORMATTING) + + # TODO: use Time.xmlschema instead of Time.parse; + # use regexp instead of Date.parse + unless defined?(XML_PARSING) + XML_PARSING = { + "symbol" => Proc.new { |symbol| symbol.to_sym }, + "date" => Proc.new { |date| ::Date.parse(date) }, + "datetime" => Proc.new { |time| ::Time.parse(time).utc rescue ::DateTime.parse(time).utc }, + "integer" => Proc.new { |integer| integer.to_i }, + "float" => Proc.new { |float| float.to_f }, + "decimal" => Proc.new { |number| BigDecimal(number) }, + "boolean" => Proc.new { |boolean| %w(1 true).include?(boolean.strip) }, + "string" => Proc.new { |string| string.to_s }, + "yaml" => Proc.new { |yaml| YAML::load(yaml) rescue yaml }, + "base64Binary" => Proc.new { |bin| ActiveSupport::Base64.decode64(bin) }, + "file" => Proc.new do |file, entity| + f = StringIO.new(ActiveSupport::Base64.decode64(file)) + f.extend(FileLike) + f.original_filename = entity['name'] + f.content_type = entity['content_type'] + f + end + } - # Converts a hash into a string suitable for use as a URL query string. An optional <tt>namespace</tt> can be - # passed to enclose the param names (see example below). - # - # ==== Examples - # { :name => 'David', :nationality => 'Danish' }.to_query # => "name=David&nationality=Danish" - # - # { :name => 'David', :nationality => 'Danish' }.to_query('user') # => "user%5Bname%5D=David&user%5Bnationality%5D=Danish" - def to_query(namespace = nil) - collect do |key, value| - value.to_query(namespace ? "#{namespace}[#{key}]" : key) - end.sort * '&' - end - - alias_method :to_param, :to_query - - def to_xml(options = {}) - require 'builder' unless defined?(Builder) - - options[:indent] ||= 2 - options.reverse_merge!({ :builder => Builder::XmlMarkup.new(:indent => options[:indent]), - :root => "hash" }) - options[:builder].instruct! unless options.delete(:skip_instruct) - root = rename_key(options[:root].to_s, options) - - options[:builder].__send__(:method_missing, root) do - each do |key, value| - case value - when ::Hash - value.to_xml(options.merge({ :root => key, :skip_instruct => true })) - when ::Array - value.to_xml(options.merge({ :root => key, :children => key.to_s.singularize, :skip_instruct => true})) - when ::Method, ::Proc - # If the Method or Proc takes two arguments, then - # pass the suggested child element name. This is - # used if the Method or Proc will be operating over - # multiple records and needs to create an containing - # element that will contain the objects being - # serialized. - if 1 == value.arity - value.call(options.merge({ :root => key, :skip_instruct => true })) - else - value.call(options.merge({ :root => key, :skip_instruct => true }), key.to_s.singularize) - end - else - if value.respond_to?(:to_xml) - value.to_xml(options.merge({ :root => key, :skip_instruct => true })) - else - type_name = XML_TYPE_NAMES[value.class.name] - - key = rename_key(key.to_s, options) - - attributes = options[:skip_types] || value.nil? || type_name.nil? ? { } : { :type => type_name } - if value.nil? - attributes[:nil] = true - end - - options[:builder].tag!(key, - XML_FORMATTING[type_name] ? XML_FORMATTING[type_name].call(value) : value, - attributes - ) - end - end - end - - yield options[:builder] if block_given? - end + XML_PARSING.update( + "double" => XML_PARSING["float"], + "dateTime" => XML_PARSING["datetime"] + ) + end - end + # Converts a hash into a string suitable for use as a URL query string. An optional <tt>namespace</tt> can be + # passed to enclose the param names (see example below). + # + # ==== Examples + # { :name => 'David', :nationality => 'Danish' }.to_query # => "name=David&nationality=Danish" + # + # { :name => 'David', :nationality => 'Danish' }.to_query('user') # => "user%5Bname%5D=David&user%5Bnationality%5D=Danish" + def to_query(namespace = nil) + collect do |key, value| + value.to_query(namespace ? "#{namespace}[#{key}]" : key) + end.sort * '&' + end + + alias_method :to_param, :to_query + + def to_xml(options = {}) + require 'builder' unless defined?(Builder) + + options[:indent] ||= 2 + options.reverse_merge!({ :builder => Builder::XmlMarkup.new(:indent => options[:indent]), + :root => "hash" }) + options[:builder].instruct! unless options.delete(:skip_instruct) + root = rename_key(options[:root].to_s, options) + + options[:builder].__send__(:method_missing, root) do + each do |key, value| + case value + when ::Hash + value.to_xml(options.merge({ :root => key, :skip_instruct => true })) + when ::Array + value.to_xml(options.merge({ :root => key, :children => key.to_s.singularize, :skip_instruct => true})) + when ::Method, ::Proc + # If the Method or Proc takes two arguments, then + # pass the suggested child element name. This is + # used if the Method or Proc will be operating over + # multiple records and needs to create an containing + # element that will contain the objects being + # serialized. + if 1 == value.arity + value.call(options.merge({ :root => key, :skip_instruct => true })) + else + value.call(options.merge({ :root => key, :skip_instruct => true }), key.to_s.singularize) + end + else + if value.respond_to?(:to_xml) + value.to_xml(options.merge({ :root => key, :skip_instruct => true })) + else + type_name = XML_TYPE_NAMES[value.class.name] - def rename_key(key, options = {}) - camelize = options.has_key?(:camelize) && options[:camelize] - dasherize = !options.has_key?(:dasherize) || options[:dasherize] - key = key.camelize if camelize - dasherize ? key.dasherize : key - end + key = rename_key(key.to_s, options) - module ClassMethods - def from_xml(xml) - typecast_xml_value(unrename_keys(XmlMini.parse(xml))) - end - - private - def typecast_xml_value(value) - case value.class.to_s - when 'Hash' - if value['type'] == 'array' - child_key, entries = value.detect { |k,v| k != 'type' } # child_key is throwaway - if entries.nil? || (c = value['__content__'] && c.blank?) - [] - else - case entries.class.to_s # something weird with classes not matching here. maybe singleton methods breaking is_a? - when "Array" - entries.collect { |v| typecast_xml_value(v) } - when "Hash" - [typecast_xml_value(entries)] - else - raise "can't typecast #{entries.inspect}" - end - end - elsif value.has_key?("__content__") - content = value["__content__"] - if parser = XML_PARSING[value["type"]] - if parser.arity == 2 - XML_PARSING[value["type"]].call(content, value) - else - XML_PARSING[value["type"]].call(content) - end - else - content - end - elsif value['type'] == 'string' && value['nil'] != 'true' - "" - # blank or nil parsed values are represented by nil - elsif value.blank? || value['nil'] == 'true' - nil - # If the type is the only element which makes it then - # this still makes the value nil, except if type is - # a XML node(where type['value'] is a Hash) - elsif value['type'] && value.size == 1 && !value['type'].is_a?(::Hash) - nil - else - xml_value = value.inject({}) do |h,(k,v)| - h[k] = typecast_xml_value(v) - h - end - - # Turn { :files => { :file => #<StringIO> } into { :files => #<StringIO> } so it is compatible with - # how multipart uploaded files from HTML appear - xml_value["file"].is_a?(StringIO) ? xml_value["file"] : xml_value - end - when 'Array' - value.map! { |i| typecast_xml_value(i) } - case value.length - when 0 then nil - when 1 then value.first - else value - end - when 'String' - value - else - raise "can't typecast #{value.class.name} - #{value.inspect}" + attributes = options[:skip_types] || value.nil? || type_name.nil? ? { } : { :type => type_name } + if value.nil? + attributes[:nil] = true end + + options[:builder].tag!(key, + XML_FORMATTING[type_name] ? XML_FORMATTING[type_name].call(value) : value, + attributes + ) end + end + end + + yield options[:builder] if block_given? + end - def unrename_keys(params) - case params.class.to_s - when "Hash" - params.inject({}) do |h,(k,v)| - h[k.to_s.underscore.tr("-", "_")] = unrename_keys(v) - h - end + end + + def rename_key(key, options = {}) + camelize = options.has_key?(:camelize) && options[:camelize] + dasherize = !options.has_key?(:dasherize) || options[:dasherize] + key = key.camelize if camelize + dasherize ? key.dasherize : key + end + + class << self + def from_xml(xml) + typecast_xml_value(unrename_keys(ActiveSupport::XmlMini.parse(xml))) + end + + private + def typecast_xml_value(value) + case value.class.to_s + when 'Hash' + if value['type'] == 'array' + child_key, entries = value.detect { |k,v| k != 'type' } # child_key is throwaway + if entries.nil? || (c = value['__content__'] && c.blank?) + [] + else + case entries.class.to_s # something weird with classes not matching here. maybe singleton methods breaking is_a? when "Array" - params.map { |v| unrename_keys(v) } + entries.collect { |v| typecast_xml_value(v) } + when "Hash" + [typecast_xml_value(entries)] else - params + raise "can't typecast #{entries.inspect}" + end end + elsif value.has_key?("__content__") + content = value["__content__"] + if parser = XML_PARSING[value["type"]] + if parser.arity == 2 + XML_PARSING[value["type"]].call(content, value) + else + XML_PARSING[value["type"]].call(content) + end + else + content + end + elsif value['type'] == 'string' && value['nil'] != 'true' + "" + # blank or nil parsed values are represented by nil + elsif value.blank? || value['nil'] == 'true' + nil + # If the type is the only element which makes it then + # this still makes the value nil, except if type is + # a XML node(where type['value'] is a Hash) + elsif value['type'] && value.size == 1 && !value['type'].is_a?(::Hash) + nil + else + xml_value = value.inject({}) do |h,(k,v)| + h[k] = typecast_xml_value(v) + h + end + + # Turn { :files => { :file => #<StringIO> } into { :files => #<StringIO> } so it is compatible with + # how multipart uploaded files from HTML appear + xml_value["file"].is_a?(StringIO) ? xml_value["file"] : xml_value end + when 'Array' + value.map! { |i| typecast_xml_value(i) } + case value.length + when 0 then nil + when 1 then value.first + else value + end + when 'String' + value + else + raise "can't typecast #{value.class.name} - #{value.inspect}" + end + end + + def unrename_keys(params) + case params.class.to_s + when "Hash" + params.inject({}) do |h,(k,v)| + h[k.to_s.underscore.tr("-", "_")] = unrename_keys(v) + h + end + when "Array" + params.map { |v| unrename_keys(v) } + else + params end end - end end end diff --git a/activesupport/lib/active_support/core_ext/hash/deep_merge.rb b/activesupport/lib/active_support/core_ext/hash/deep_merge.rb index f8842ba57a..b009be3d84 100644 --- a/activesupport/lib/active_support/core_ext/hash/deep_merge.rb +++ b/activesupport/lib/active_support/core_ext/hash/deep_merge.rb @@ -1,23 +1,16 @@ -module ActiveSupport #:nodoc: - module CoreExtensions #:nodoc: - module Hash #:nodoc: - # Allows for deep merging - module DeepMerge - # Returns a new hash with +self+ and +other_hash+ merged recursively. - def deep_merge(other_hash) - self.merge(other_hash) do |key, oldval, newval| - oldval = oldval.to_hash if oldval.respond_to?(:to_hash) - newval = newval.to_hash if newval.respond_to?(:to_hash) - oldval.class.to_s == 'Hash' && newval.class.to_s == 'Hash' ? oldval.deep_merge(newval) : newval - end - end - - # Returns a new hash with +self+ and +other_hash+ merged recursively. - # Modifies the receiver in place. - def deep_merge!(other_hash) - replace(deep_merge(other_hash)) - end - end +class Hash + # Returns a new hash with +self+ and +other_hash+ merged recursively. + def deep_merge(other_hash) + merge(other_hash) do |key, oldval, newval| + oldval = oldval.to_hash if oldval.respond_to?(:to_hash) + newval = newval.to_hash if newval.respond_to?(:to_hash) + oldval.class.to_s == 'Hash' && newval.class.to_s == 'Hash' ? oldval.deep_merge(newval) : newval end end + + # Returns a new hash with +self+ and +other_hash+ merged recursively. + # Modifies the receiver in place. + def deep_merge!(other_hash) + replace(deep_merge(other_hash)) + end end diff --git a/activesupport/lib/active_support/core_ext/hash/diff.rb b/activesupport/lib/active_support/core_ext/hash/diff.rb index 6abd678822..da98593458 100644 --- a/activesupport/lib/active_support/core_ext/hash/diff.rb +++ b/activesupport/lib/active_support/core_ext/hash/diff.rb @@ -1,19 +1,13 @@ -module ActiveSupport #:nodoc: - module CoreExtensions #:nodoc: - module Hash #:nodoc: - module Diff - # Returns a hash that represents the difference between two hashes. - # - # Examples: - # - # {1 => 2}.diff(1 => 2) # => {} - # {1 => 2}.diff(1 => 3) # => {1 => 2} - # {}.diff(1 => 2) # => {1 => 2} - # {1 => 2, 3 => 4}.diff(1 => 2) # => {3 => 4} - def diff(h2) - self.dup.delete_if { |k, v| h2[k] == v }.merge(h2.dup.delete_if { |k, v| self.has_key?(k) }) - end - end - end +class Hash + # Returns a hash that represents the difference between two hashes. + # + # Examples: + # + # {1 => 2}.diff(1 => 2) # => {} + # {1 => 2}.diff(1 => 3) # => {1 => 2} + # {}.diff(1 => 2) # => {1 => 2} + # {1 => 2, 3 => 4}.diff(1 => 2) # => {3 => 4} + def diff(h2) + dup.delete_if { |k, v| h2[k] == v }.merge(h2.dup.delete_if { |k, v| has_key?(k) }) 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 949976d741..6d04cb5621 100644 --- a/activesupport/lib/active_support/core_ext/hash/except.rb +++ b/activesupport/lib/active_support/core_ext/hash/except.rb @@ -1,25 +1,16 @@ -require 'set' - -module ActiveSupport #:nodoc: - module CoreExtensions #:nodoc: - module Hash #:nodoc: - # Return a hash that includes everything but the given keys. This is useful for - # limiting a set of parameters to everything but a few known toggles: - # - # @person.update_attributes(params[:person].except(:admin)) - module Except - # Returns a new hash without the given keys. - def except(*keys) - dup.except!(*keys) - end +class Hash + # Return a hash that includes everything but the given keys. This is useful for + # limiting a set of parameters to everything but a few known toggles: + # + # @person.update_attributes(params[:person].except(:admin)) + def except(*keys) + dup.except!(*keys) + end - # Replaces the hash without the given keys. - def except!(*keys) - keys.map! { |key| convert_key(key) } if respond_to?(:convert_key) - keys.each { |key| delete(key) } - self - end - end - end + # Replaces the hash without the given keys. + def except!(*keys) + keys.map! { |key| convert_key(key) } if respond_to?(:convert_key) + keys.each { |key| delete(key) } + self end end diff --git a/activesupport/lib/active_support/core_ext/hash/indifferent_access.rb b/activesupport/lib/active_support/core_ext/hash/indifferent_access.rb index 34ba8a005d..b30e1602b6 100644 --- a/activesupport/lib/active_support/core_ext/hash/indifferent_access.rb +++ b/activesupport/lib/active_support/core_ext/hash/indifferent_access.rb @@ -1,143 +1,9 @@ -# This class has dubious semantics and we only have it so that -# people can write params[:key] instead of params['key'] -# and they get the same value for both keys. +require 'active_support/hash_with_indifferent_access' -class HashWithIndifferentAccess < Hash - def initialize(constructor = {}) - if constructor.is_a?(Hash) - super() - update(constructor) - else - super(constructor) - end - end - - def default(key = nil) - if key.is_a?(Symbol) && include?(key = key.to_s) - self[key] - else - super - end - end - - alias_method :regular_writer, :[]= unless method_defined?(:regular_writer) - alias_method :regular_update, :update unless method_defined?(:regular_update) - - # Assigns a new value to the hash: - # - # hash = HashWithIndifferentAccess.new - # hash[:key] = "value" - # - def []=(key, value) - regular_writer(convert_key(key), convert_value(value)) - end - - # Updates the instantized hash with values from the second: - # - # hash_1 = HashWithIndifferentAccess.new - # hash_1[:key] = "value" - # - # hash_2 = HashWithIndifferentAccess.new - # hash_2[:key] = "New Value!" - # - # hash_1.update(hash_2) # => {"key"=>"New Value!"} - # - def update(other_hash) - other_hash.each_pair { |key, value| regular_writer(convert_key(key), convert_value(value)) } - self - end - - alias_method :merge!, :update - - # Checks the hash for a key matching the argument passed in: - # - # hash = HashWithIndifferentAccess.new - # hash["key"] = "value" - # hash.key? :key # => true - # hash.key? "key" # => true - # - def key?(key) - super(convert_key(key)) - end - - alias_method :include?, :key? - alias_method :has_key?, :key? - alias_method :member?, :key? - - # Fetches the value for the specified key, same as doing hash[key] - def fetch(key, *extras) - super(convert_key(key), *extras) - end - - # Returns an array of the values at the specified indices: - # - # hash = HashWithIndifferentAccess.new - # hash[:a] = "x" - # hash[:b] = "y" - # hash.values_at("a", "b") # => ["x", "y"] - # - def values_at(*indices) - indices.collect {|key| self[convert_key(key)]} - end - - # Returns an exact copy of the hash. - def dup - HashWithIndifferentAccess.new(self) - end - - # Merges the instantized and the specified hashes together, giving precedence to the values from the second hash - # Does not overwrite the existing hash. - def merge(hash) - self.dup.update(hash) - end - - # Performs the opposite of merge, with the keys and values from the first hash taking precedence over the second. - # This overloaded definition prevents returning a regular hash, if reverse_merge is called on a HashWithDifferentAccess. - def reverse_merge(other_hash) - super other_hash.with_indifferent_access - end - - # Removes a specified key from the hash. - def delete(key) - super(convert_key(key)) - end - - def stringify_keys!; self end - def symbolize_keys!; self end - def to_options!; self end - - # Convert to a Hash with String keys. - def to_hash - Hash.new(default).merge(self) - end - - protected - def convert_key(key) - key.kind_of?(Symbol) ? key.to_s : key - end - - def convert_value(value) - case value - when Hash - value.with_indifferent_access - when Array - value.collect { |e| e.is_a?(Hash) ? e.with_indifferent_access : e } - else - value - end - end -end - -module ActiveSupport #:nodoc: - module CoreExtensions #:nodoc: - module Hash #:nodoc: - module IndifferentAccess #:nodoc: - def with_indifferent_access - hash = HashWithIndifferentAccess.new(self) - hash.default = self.default - hash - end - end - end +class Hash + def with_indifferent_access + hash = ActiveSupport::HashWithIndifferentAccess.new(self) + hash.default = self.default + hash end end diff --git a/activesupport/lib/active_support/core_ext/hash/keys.rb b/activesupport/lib/active_support/core_ext/hash/keys.rb index af9d372d76..ffaa69570f 100644 --- a/activesupport/lib/active_support/core_ext/hash/keys.rb +++ b/activesupport/lib/active_support/core_ext/hash/keys.rb @@ -1,52 +1,46 @@ -module ActiveSupport #:nodoc: - module CoreExtensions #:nodoc: - module Hash #:nodoc: - module Keys - # Return a new hash with all keys converted to strings. - def stringify_keys - inject({}) do |options, (key, value)| - options[key.to_s] = value - options - end - end +class Hash + # Return a new hash with all keys converted to strings. + def stringify_keys + inject({}) do |options, (key, value)| + options[key.to_s] = value + options + end + end - # Destructively convert all keys to strings. - def stringify_keys! - keys.each do |key| - self[key.to_s] = delete(key) - end - self - end + # Destructively convert all keys to strings. + def stringify_keys! + keys.each do |key| + self[key.to_s] = delete(key) + end + self + end - # Return a new hash with all keys converted to symbols. - def symbolize_keys - inject({}) do |options, (key, value)| - options[(key.to_sym rescue key) || key] = value - options - end - end + # Return a new hash with all keys converted to symbols. + def symbolize_keys + inject({}) do |options, (key, value)| + options[(key.to_sym rescue key) || key] = value + options + end + end - # Destructively convert all keys to symbols. - def symbolize_keys! - self.replace(self.symbolize_keys) - end + # Destructively convert all keys to symbols. + def symbolize_keys! + self.replace(self.symbolize_keys) + end - alias_method :to_options, :symbolize_keys - alias_method :to_options!, :symbolize_keys! + alias_method :to_options, :symbolize_keys + alias_method :to_options!, :symbolize_keys! - # Validate all keys in a hash match *valid keys, raising ArgumentError on a mismatch. - # Note that keys are NOT treated indifferently, meaning if you use strings for keys but assert symbols - # as keys, this will fail. - # - # ==== Examples - # { :name => "Rob", :years => "28" }.assert_valid_keys(:name, :age) # => raises "ArgumentError: Unknown key(s): years" - # { :name => "Rob", :age => "28" }.assert_valid_keys("name", "age") # => raises "ArgumentError: Unknown key(s): name, age" - # { :name => "Rob", :age => "28" }.assert_valid_keys(:name, :age) # => passes, raises nothing - def assert_valid_keys(*valid_keys) - unknown_keys = keys - [valid_keys].flatten - raise(ArgumentError, "Unknown key(s): #{unknown_keys.join(", ")}") unless unknown_keys.empty? - end - end - end + # Validate all keys in a hash match *valid keys, raising ArgumentError on a mismatch. + # Note that keys are NOT treated indifferently, meaning if you use strings for keys but assert symbols + # as keys, this will fail. + # + # ==== Examples + # { :name => "Rob", :years => "28" }.assert_valid_keys(:name, :age) # => raises "ArgumentError: Unknown key(s): years" + # { :name => "Rob", :age => "28" }.assert_valid_keys("name", "age") # => raises "ArgumentError: Unknown key(s): name, age" + # { :name => "Rob", :age => "28" }.assert_valid_keys(:name, :age) # => passes, raises nothing + def assert_valid_keys(*valid_keys) + unknown_keys = keys - [valid_keys].flatten + raise(ArgumentError, "Unknown key(s): #{unknown_keys.join(", ")}") unless unknown_keys.empty? end end diff --git a/activesupport/lib/active_support/core_ext/hash/reverse_merge.rb b/activesupport/lib/active_support/core_ext/hash/reverse_merge.rb index 546e261cc9..ebfdcb2cf0 100644 --- a/activesupport/lib/active_support/core_ext/hash/reverse_merge.rb +++ b/activesupport/lib/active_support/core_ext/hash/reverse_merge.rb @@ -1,35 +1,28 @@ -module ActiveSupport #:nodoc: - module CoreExtensions #:nodoc: - module Hash #:nodoc: - # Allows for reverse merging two hashes where the keys in the calling hash take precedence over those - # in the <tt>other_hash</tt>. This is particularly useful for initializing an option hash with default values: - # - # def setup(options = {}) - # options.reverse_merge! :size => 25, :velocity => 10 - # end - # - # Using <tt>merge</tt>, the above example would look as follows: - # - # def setup(options = {}) - # { :size => 25, :velocity => 10 }.merge(options) - # end - # - # The default <tt>:size</tt> and <tt>:velocity</tt> are only set if the +options+ hash passed in doesn't already - # have the respective key. - module ReverseMerge - # Performs the opposite of <tt>merge</tt>, with the keys and values from the first hash taking precedence over the second. - def reverse_merge(other_hash) - other_hash.merge(self) - end - - # Performs the opposite of <tt>merge</tt>, with the keys and values from the first hash taking precedence over the second. - # Modifies the receiver in place. - def reverse_merge!(other_hash) - replace(reverse_merge(other_hash)) - end +class Hash + # Allows for reverse merging two hashes where the keys in the calling hash take precedence over those + # in the <tt>other_hash</tt>. This is particularly useful for initializing an option hash with default values: + # + # def setup(options = {}) + # options.reverse_merge! :size => 25, :velocity => 10 + # end + # + # Using <tt>merge</tt>, the above example would look as follows: + # + # def setup(options = {}) + # { :size => 25, :velocity => 10 }.merge(options) + # end + # + # The default <tt>:size</tt> and <tt>:velocity</tt> are only set if the +options+ hash passed in doesn't already + # have the respective key. + def reverse_merge(other_hash) + other_hash.merge(self) + end - alias_method :reverse_update, :reverse_merge! - end - end + # Performs the opposite of <tt>merge</tt>, with the keys and values from the first hash taking precedence over the second. + # Modifies the receiver in place. + def reverse_merge!(other_hash) + replace(reverse_merge(other_hash)) end + + alias_method :reverse_update, :reverse_merge! end diff --git a/activesupport/lib/active_support/core_ext/hash/slice.rb b/activesupport/lib/active_support/core_ext/hash/slice.rb index d845a6d8ca..7aa394d7bf 100644 --- a/activesupport/lib/active_support/core_ext/hash/slice.rb +++ b/activesupport/lib/active_support/core_ext/hash/slice.rb @@ -1,40 +1,32 @@ -module ActiveSupport #:nodoc: - module CoreExtensions #:nodoc: - module Hash #:nodoc: - # Slice a hash to include only the given keys. This is useful for - # limiting an options hash to valid keys before passing to a method: - # - # def search(criteria = {}) - # assert_valid_keys(:mass, :velocity, :time) - # end - # - # search(options.slice(:mass, :velocity, :time)) - # - # If you have an array of keys you want to limit to, you should splat them: - # - # valid_keys = [:mass, :velocity, :time] - # search(options.slice(*valid_keys)) - module Slice - # Returns a new hash with only the given keys. - def slice(*keys) - keys = keys.map! { |key| convert_key(key) } if respond_to?(:convert_key) - hash = self.class.new - keys.each { |k| hash[k] = self[k] if has_key?(k) } - hash - end +class Hash + # Slice a hash to include only the given keys. This is useful for + # limiting an options hash to valid keys before passing to a method: + # + # def search(criteria = {}) + # assert_valid_keys(:mass, :velocity, :time) + # end + # + # search(options.slice(:mass, :velocity, :time)) + # + # If you have an array of keys you want to limit to, you should splat them: + # + # valid_keys = [:mass, :velocity, :time] + # search(options.slice(*valid_keys)) + def slice(*keys) + keys = keys.map! { |key| convert_key(key) } if respond_to?(:convert_key) + hash = self.class.new + keys.each { |k| hash[k] = self[k] if has_key?(k) } + hash + end - # Replaces the hash with only the given keys. - # Returns a hash contained the removed key/value pairs - # {:a => 1, :b => 2, :c => 3, :d => 4}.slice!(:a, :b) # => {:c => 3, :d =>4} - def slice!(*keys) - keys = keys.map! { |key| convert_key(key) } if respond_to?(:convert_key) - omit = slice(*self.keys - keys) - hash = slice(*keys) - replace(hash) - omit - end - end - end + # Replaces the hash with only the given keys. + # Returns a hash contained the removed key/value pairs + # {:a => 1, :b => 2, :c => 3, :d => 4}.slice!(:a, :b) # => {:c => 3, :d =>4} + def slice!(*keys) + keys = keys.map! { |key| convert_key(key) } if respond_to?(:convert_key) + omit = slice(*self.keys - keys) + hash = slice(*keys) + replace(hash) + omit end end - diff --git a/activesupport/lib/active_support/core_ext/integer.rb b/activesupport/lib/active_support/core_ext/integer.rb index 18a7a402db..7ba018ed7f 100644 --- a/activesupport/lib/active_support/core_ext/integer.rb +++ b/activesupport/lib/active_support/core_ext/integer.rb @@ -1,9 +1,3 @@ require 'active_support/core_ext/integer/even_odd' require 'active_support/core_ext/integer/inflections' require 'active_support/core_ext/integer/time' - -class Integer #:nodoc: - include ActiveSupport::CoreExtensions::Integer::EvenOdd - include ActiveSupport::CoreExtensions::Integer::Inflections - include ActiveSupport::CoreExtensions::Integer::Time -end diff --git a/activesupport/lib/active_support/core_ext/integer/even_odd.rb b/activesupport/lib/active_support/core_ext/integer/even_odd.rb index b1d1e28062..6d005268a3 100644 --- a/activesupport/lib/active_support/core_ext/integer/even_odd.rb +++ b/activesupport/lib/active_support/core_ext/integer/even_odd.rb @@ -1,29 +1,16 @@ -module ActiveSupport #:nodoc: - module CoreExtensions #:nodoc: - module Integer #:nodoc: - # For checking if a fixnum is even or odd. - # - # 2.even? # => true - # 2.odd? # => false - # 1.even? # => false - # 1.odd? # => true - # 0.even? # => true - # 0.odd? # => false - # -1.even? # => false - # -1.odd? # => true - module EvenOdd - def multiple_of?(number) - self % number == 0 - end +class Integer + # Check whether the integer is evenly divisible by the argument. + def multiple_of?(number) + self % number == 0 + end - def even? - multiple_of? 2 - end if RUBY_VERSION < '1.9' + # Is the integer a multiple of 2? + def even? + multiple_of? 2 + end if RUBY_VERSION < '1.9' - def odd? - !even? - end if RUBY_VERSION < '1.9' - end - end - end + # Is the integer not a multiple of 2? + def odd? + !even? + end if RUBY_VERSION < '1.9' end diff --git a/activesupport/lib/active_support/core_ext/integer/inflections.rb b/activesupport/lib/active_support/core_ext/integer/inflections.rb index 804702beb2..e81e7af436 100644 --- a/activesupport/lib/active_support/core_ext/integer/inflections.rb +++ b/activesupport/lib/active_support/core_ext/integer/inflections.rb @@ -1,20 +1,14 @@ require 'active_support/inflector' -module ActiveSupport #:nodoc: - module CoreExtensions #:nodoc: - module Integer #:nodoc: - module Inflections - # Ordinalize turns a number into an ordinal string used to denote the - # position in an ordered sequence such as 1st, 2nd, 3rd, 4th. - # - # 1.ordinalize # => "1st" - # 2.ordinalize # => "2nd" - # 1002.ordinalize # => "1002nd" - # 1003.ordinalize # => "1003rd" - def ordinalize - Inflector.ordinalize(self) - end - end - end +class Integer + # Ordinalize turns a number into an ordinal string used to denote the + # position in an ordered sequence such as 1st, 2nd, 3rd, 4th. + # + # 1.ordinalize # => "1st" + # 2.ordinalize # => "2nd" + # 1002.ordinalize # => "1002nd" + # 1003.ordinalize # => "1003rd" + def ordinalize + ActiveSupport::Inflector.ordinalize(self) end end diff --git a/activesupport/lib/active_support/core_ext/integer/time.rb b/activesupport/lib/active_support/core_ext/integer/time.rb index 356e145b90..a0ccf0e971 100644 --- a/activesupport/lib/active_support/core_ext/integer/time.rb +++ b/activesupport/lib/active_support/core_ext/integer/time.rb @@ -1,45 +1,39 @@ -module ActiveSupport #:nodoc: - module CoreExtensions #:nodoc: - module Integer #:nodoc: - # Enables the use of time calculations and declarations, like 45.minutes + 2.hours + 4.years. - # - # These methods use Time#advance for precise date calculations when using from_now, ago, etc. - # as well as adding or subtracting their results from a Time object. For example: - # - # # equivalent to Time.now.advance(:months => 1) - # 1.month.from_now - # - # # equivalent to Time.now.advance(:years => 2) - # 2.years.from_now - # - # # equivalent to Time.now.advance(:months => 4, :years => 5) - # (4.months + 5.years).from_now - # - # While these methods provide precise calculation when used as in the examples above, care - # should be taken to note that this is not true if the result of `months', `years', etc is - # converted before use: - # - # # equivalent to 30.days.to_i.from_now - # 1.month.to_i.from_now - # - # # equivalent to 365.25.days.to_f.from_now - # 1.year.to_f.from_now - # - # In such cases, Ruby's core - # Date[http://stdlib.rubyonrails.org/libdoc/date/rdoc/index.html] and - # Time[http://stdlib.rubyonrails.org/libdoc/time/rdoc/index.html] should be used for precision - # date and time arithmetic - module Time - def months - ActiveSupport::Duration.new(self * 30.days, [[:months, self]]) - end - alias :month :months +class Integer + # Enables the use of time calculations and declarations, like 45.minutes + 2.hours + 4.years. + # + # These methods use Time#advance for precise date calculations when using from_now, ago, etc. + # as well as adding or subtracting their results from a Time object. For example: + # + # # equivalent to Time.now.advance(:months => 1) + # 1.month.from_now + # + # # equivalent to Time.now.advance(:years => 2) + # 2.years.from_now + # + # # equivalent to Time.now.advance(:months => 4, :years => 5) + # (4.months + 5.years).from_now + # + # While these methods provide precise calculation when used as in the examples above, care + # should be taken to note that this is not true if the result of `months', `years', etc is + # converted before use: + # + # # equivalent to 30.days.to_i.from_now + # 1.month.to_i.from_now + # + # # equivalent to 365.25.days.to_f.from_now + # 1.year.to_f.from_now + # + # In such cases, Ruby's core + # Date[http://stdlib.rubyonrails.org/libdoc/date/rdoc/index.html] and + # Time[http://stdlib.rubyonrails.org/libdoc/time/rdoc/index.html] should be used for precision + # date and time arithmetic + def months + ActiveSupport::Duration.new(self * 30.days, [[:months, self]]) + end + alias :month :months - def years - ActiveSupport::Duration.new(self * 365.25.days, [[:years, self]]) - end - alias :year :years - end - end + def years + ActiveSupport::Duration.new(self * 365.25.days, [[:years, self]]) end + alias :year :years end diff --git a/activesupport/lib/active_support/core_ext/kernel/reporting.rb b/activesupport/lib/active_support/core_ext/kernel/reporting.rb index 0f101e8fa4..d9b84e6543 100644 --- a/activesupport/lib/active_support/core_ext/kernel/reporting.rb +++ b/activesupport/lib/active_support/core_ext/kernel/reporting.rb @@ -7,15 +7,17 @@ module Kernel # # noisy_call # warning voiced def silence_warnings - old_verbose, $VERBOSE = $VERBOSE, nil - yield - ensure - $VERBOSE = old_verbose + with_warnings(nil) { yield } end # Sets $VERBOSE to true for the duration of the block and back to its original value afterwards. def enable_warnings - old_verbose, $VERBOSE = $VERBOSE, true + with_warnings(true) { yield } + end + + # Sets $VERBOSE for the duration of the block and back to its original value afterwards. + def with_warnings(flag) + old_verbose, $VERBOSE = $VERBOSE, flag yield ensure $VERBOSE = old_verbose @@ -56,4 +58,4 @@ module Kernel raise unless exception_classes.any? { |cls| e.kind_of?(cls) } end end -end
\ No newline at end of file +end diff --git a/activesupport/lib/active_support/core_ext/load_error.rb b/activesupport/lib/active_support/core_ext/load_error.rb index 6165e95443..f36a21818f 100644 --- a/activesupport/lib/active_support/core_ext/load_error.rb +++ b/activesupport/lib/active_support/core_ext/load_error.rb @@ -24,15 +24,12 @@ class MissingSourceFile < LoadError #:nodoc: ] unless defined?(REGEXPS) end -module ActiveSupport #:nodoc: - module CoreExtensions #:nodoc: - module LoadErrorExtensions #:nodoc: - module LoadErrorClassMethods #:nodoc: - def new(*args) - (self == LoadError && MissingSourceFile.from_message(args.first)) || super - end - end - ::LoadError.extend(LoadErrorClassMethods) +class LoadError + def self.new(*args) + if self == LoadError + MissingSourceFile.from_message(args.first) + else + super end end end diff --git a/activesupport/lib/active_support/core_ext/module.rb b/activesupport/lib/active_support/core_ext/module.rb index cb31437094..c37c9badca 100644 --- a/activesupport/lib/active_support/core_ext/module.rb +++ b/activesupport/lib/active_support/core_ext/module.rb @@ -1,11 +1,12 @@ +require 'active_support/core_ext/module/aliasing' +require 'active_support/core_ext/module/introspection' + require 'active_support/core_ext/module/inclusion' require 'active_support/core_ext/module/attribute_accessors' require 'active_support/core_ext/module/attr_internal' require 'active_support/core_ext/module/attr_accessor_with_default' require 'active_support/core_ext/module/delegation' -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' require 'active_support/core_ext/module/setup' diff --git a/activesupport/lib/active_support/core_ext/module/aliasing.rb b/activesupport/lib/active_support/core_ext/module/aliasing.rb index 10fa520ba1..3cad164148 100644 --- a/activesupport/lib/active_support/core_ext/module/aliasing.rb +++ b/activesupport/lib/active_support/core_ext/module/aliasing.rb @@ -1,74 +1,70 @@ -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? +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}" + with_method, without_method = "#{aliased_target}_with_#{feature}#{punctuation}", "#{aliased_target}_without_#{feature}#{punctuation}" - alias_method without_method, target - alias_method target, with_method + 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 subject; self.title; end - def #{new_name}?; self.#{old_name}?; end # def subject?; self.title?; end - def #{new_name}=(v); self.#{old_name} = v; end # def subject=(v); self.title = v; end - STR - end + 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 subject; self.title; end + def #{new_name}?; self.#{old_name}?; end # def subject?; self.title?; end + def #{new_name}=(v); self.#{old_name} = v; end # def subject=(v); self.title = v; end + STR + end end diff --git a/activesupport/lib/active_support/core_ext/module/attribute_accessors.rb b/activesupport/lib/active_support/core_ext/module/attribute_accessors.rb index 137590d286..131b512944 100644 --- a/activesupport/lib/active_support/core_ext/module/attribute_accessors.rb +++ b/activesupport/lib/active_support/core_ext/module/attribute_accessors.rb @@ -1,35 +1,25 @@ -# Extends the module object with module and instance accessors for class attributes, -# just like the native attr* accessors for instance attributes. -# -# module AppConfiguration -# mattr_accessor :google_api_key -# self.google_api_key = "123456789" -# -# mattr_accessor :paypal_url -# self.paypal_url = "www.sandbox.paypal.com" -# end -# -# AppConfiguration.google_api_key = "overriding the api key!" +require 'active_support/core_ext/array/extract_options' + class Module def mattr_reader(*syms) + syms.extract_options! syms.each do |sym| - next if sym.is_a?(Hash) class_eval(<<-EOS, __FILE__, __LINE__ + 1) unless defined? @@#{sym} # unless defined? @@pagination_options @@#{sym} = nil # @@pagination_options = nil end # end - # + def self.#{sym} # def self.pagination_options @@#{sym} # @@pagination_options end # end - # + def #{sym} # def pagination_options @@#{sym} # @@pagination_options end # end EOS end end - + def mattr_writer(*syms) options = syms.extract_options! syms.each do |sym| @@ -37,20 +27,34 @@ class Module unless defined? @@#{sym} # unless defined? @@pagination_options @@#{sym} = nil # @@pagination_options = nil end # end - # + def self.#{sym}=(obj) # def self.pagination_options=(obj) @@#{sym} = obj # @@pagination_options = obj end # end - # - #{" # - def #{sym}=(obj) # def pagination_options=(obj) - @@#{sym} = obj # @@pagination_options = obj - end # end - " unless options[:instance_writer] == false } # # instance writer above is generated unless options[:instance_writer] == false EOS + + unless options[:instance_writer] == false + class_eval(<<-EOS, __FILE__, __LINE__) + def #{sym}=(obj) # def pagination_options=(obj) + @@#{sym} = obj # @@pagination_options = obj + end # end + EOS + end end end - + + # Extends the module object with module and instance accessors for class attributes, + # just like the native attr* accessors for instance attributes. + # + # module AppConfiguration + # mattr_accessor :google_api_key + # self.google_api_key = "123456789" + # + # mattr_accessor :paypal_url + # self.paypal_url = "www.sandbox.paypal.com" + # end + # + # AppConfiguration.google_api_key = "overriding the api key!" def mattr_accessor(*syms) mattr_reader(*syms) mattr_writer(*syms) diff --git a/activesupport/lib/active_support/core_ext/module/deprecation.rb b/activesupport/lib/active_support/core_ext/module/deprecation.rb new file mode 100644 index 0000000000..5a5b4e3f80 --- /dev/null +++ b/activesupport/lib/active_support/core_ext/module/deprecation.rb @@ -0,0 +1,9 @@ +class Module + # Declare that a method has been deprecated. + # deprecate :foo + # deprecate :bar => 'message' + # deprecate :foo, :bar, :baz => 'warning!', :qux => 'gone!' + def deprecate(*method_names) + ActiveSupport::Deprecation.deprecate_methods(self, *method_names) + 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 8beaff4b82..7708d573fa 100644 --- a/activesupport/lib/active_support/core_ext/module/introspection.rb +++ b/activesupport/lib/active_support/core_ext/module/introspection.rb @@ -1,90 +1,86 @@ -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 +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 - # 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 - end - end - parents << Object unless parents.include? Object - parents + # 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 - 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 + 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 = {} - 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 + ancestors.each do |anc| + next if anc == self + anc.constants.each { |const| inherited[const] = anc.const_get(const) } 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 } + 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 + + # 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/name_error.rb b/activesupport/lib/active_support/core_ext/name_error.rb index 49176c12d9..cd165626c8 100644 --- a/activesupport/lib/active_support/core_ext/name_error.rb +++ b/activesupport/lib/active_support/core_ext/name_error.rb @@ -1,10 +1,9 @@ -# Add a +missing_name+ method to NameError instances. -class NameError #:nodoc: - # Add a method to obtain the missing name from a NameError. +class NameError + # Extract the name of the missing constant from the exception message. def missing_name $1 if /((::)?([A-Z]\w*)(::[A-Z]\w*)*)$/ =~ message end - + # Was this exception raised because the given name was missing? def missing_name?(name) if name.is_a? Symbol diff --git a/activesupport/lib/active_support/core_ext/numeric.rb b/activesupport/lib/active_support/core_ext/numeric.rb index 759b86b81d..3805cf7990 100644 --- a/activesupport/lib/active_support/core_ext/numeric.rb +++ b/activesupport/lib/active_support/core_ext/numeric.rb @@ -1,9 +1,2 @@ -require 'active_support/core_ext/numeric/time' require 'active_support/core_ext/numeric/bytes' -require 'active_support/core_ext/numeric/conversions' - -class Numeric #:nodoc: - include ActiveSupport::CoreExtensions::Numeric::Time - include ActiveSupport::CoreExtensions::Numeric::Bytes - include ActiveSupport::CoreExtensions::Numeric::Conversions -end +require 'active_support/core_ext/numeric/time' diff --git a/activesupport/lib/active_support/core_ext/numeric/bytes.rb b/activesupport/lib/active_support/core_ext/numeric/bytes.rb index 56477673a3..507d651261 100644 --- a/activesupport/lib/active_support/core_ext/numeric/bytes.rb +++ b/activesupport/lib/active_support/core_ext/numeric/bytes.rb @@ -1,44 +1,37 @@ -module ActiveSupport #:nodoc: - module CoreExtensions #:nodoc: - module Numeric #:nodoc: - # Enables the use of byte calculations and declarations, like 45.bytes + 2.6.megabytes - module Bytes - def bytes - self - end - alias :byte :bytes +class Numeric + # Enables the use of byte calculations and declarations, like 45.bytes + 2.6.megabytes + def bytes + self + end + alias :byte :bytes - def kilobytes - self * 1024 - end - alias :kilobyte :kilobytes + def kilobytes + self * 1024 + end + alias :kilobyte :kilobytes - def megabytes - self * 1024.kilobytes - end - alias :megabyte :megabytes + def megabytes + self * 1024.kilobytes + end + alias :megabyte :megabytes - def gigabytes - self * 1024.megabytes - end - alias :gigabyte :gigabytes + def gigabytes + self * 1024.megabytes + end + alias :gigabyte :gigabytes - def terabytes - self * 1024.gigabytes - end - alias :terabyte :terabytes - - def petabytes - self * 1024.terabytes - end - alias :petabyte :petabytes - - def exabytes - self * 1024.petabytes - end - alias :exabyte :exabytes - - end - end + def terabytes + self * 1024.gigabytes + end + alias :terabyte :terabytes + + def petabytes + self * 1024.terabytes + end + alias :petabyte :petabytes + + def exabytes + self * 1024.petabytes end + alias :exabyte :exabytes end diff --git a/activesupport/lib/active_support/core_ext/numeric/conversions.rb b/activesupport/lib/active_support/core_ext/numeric/conversions.rb deleted file mode 100644 index e652ae5ca8..0000000000 --- a/activesupport/lib/active_support/core_ext/numeric/conversions.rb +++ /dev/null @@ -1,19 +0,0 @@ -module ActiveSupport #:nodoc: - module CoreExtensions #:nodoc: - module Numeric #:nodoc: - module Conversions - # Assumes self represents an offset from UTC in seconds (as returned from Time#utc_offset) - # and turns this into an +HH:MM formatted string. Example: - # - # -21_600.to_utc_offset_s # => "-06:00" - def to_utc_offset_s(colon=true) - seconds = self - sign = (seconds < 0 ? -1 : 1) - hours = seconds.abs / 3600 - minutes = (seconds.abs % 3600) / 60 - "%+03d%s%02d" % [ hours * sign, colon ? ":" : "", minutes ] - end - end - end - end -end diff --git a/activesupport/lib/active_support/core_ext/numeric/time.rb b/activesupport/lib/active_support/core_ext/numeric/time.rb index bc7f180cd1..2955e8ff1d 100644 --- a/activesupport/lib/active_support/core_ext/numeric/time.rb +++ b/activesupport/lib/active_support/core_ext/numeric/time.rb @@ -1,81 +1,75 @@ -module ActiveSupport #:nodoc: - module CoreExtensions #:nodoc: - module Numeric #:nodoc: - # Enables the use of time calculations and declarations, like 45.minutes + 2.hours + 4.years. - # - # These methods use Time#advance for precise date calculations when using from_now, ago, etc. - # as well as adding or subtracting their results from a Time object. For example: - # - # # equivalent to Time.now.advance(:months => 1) - # 1.month.from_now - # - # # equivalent to Time.now.advance(:years => 2) - # 2.years.from_now - # - # # equivalent to Time.now.advance(:months => 4, :years => 5) - # (4.months + 5.years).from_now - # - # While these methods provide precise calculation when used as in the examples above, care - # should be taken to note that this is not true if the result of `months', `years', etc is - # converted before use: - # - # # equivalent to 30.days.to_i.from_now - # 1.month.to_i.from_now - # - # # equivalent to 365.25.days.to_f.from_now - # 1.year.to_f.from_now - # - # In such cases, Ruby's core - # Date[http://stdlib.rubyonrails.org/libdoc/date/rdoc/index.html] and - # Time[http://stdlib.rubyonrails.org/libdoc/time/rdoc/index.html] should be used for precision - # date and time arithmetic - module Time - def seconds - ActiveSupport::Duration.new(self, [[:seconds, self]]) - end - alias :second :seconds - - def minutes - ActiveSupport::Duration.new(self * 60, [[:seconds, self * 60]]) - end - alias :minute :minutes - - def hours - ActiveSupport::Duration.new(self * 3600, [[:seconds, self * 3600]]) - end - alias :hour :hours - - def days - ActiveSupport::Duration.new(self * 24.hours, [[:days, self]]) - end - alias :day :days +class Numeric + # Enables the use of time calculations and declarations, like 45.minutes + 2.hours + 4.years. + # + # These methods use Time#advance for precise date calculations when using from_now, ago, etc. + # as well as adding or subtracting their results from a Time object. For example: + # + # # equivalent to Time.now.advance(:months => 1) + # 1.month.from_now + # + # # equivalent to Time.now.advance(:years => 2) + # 2.years.from_now + # + # # equivalent to Time.now.advance(:months => 4, :years => 5) + # (4.months + 5.years).from_now + # + # While these methods provide precise calculation when used as in the examples above, care + # should be taken to note that this is not true if the result of `months', `years', etc is + # converted before use: + # + # # equivalent to 30.days.to_i.from_now + # 1.month.to_i.from_now + # + # # equivalent to 365.25.days.to_f.from_now + # 1.year.to_f.from_now + # + # In such cases, Ruby's core + # Date[http://stdlib.rubyonrails.org/libdoc/date/rdoc/index.html] and + # Time[http://stdlib.rubyonrails.org/libdoc/time/rdoc/index.html] should be used for precision + # date and time arithmetic + def seconds + ActiveSupport::Duration.new(self, [[:seconds, self]]) + end + alias :second :seconds - def weeks - ActiveSupport::Duration.new(self * 7.days, [[:days, self * 7]]) - end - alias :week :weeks - - def fortnights - ActiveSupport::Duration.new(self * 2.weeks, [[:days, self * 14]]) - end - alias :fortnight :fortnights - - # Reads best without arguments: 10.minutes.ago - def ago(time = ::Time.now) - time - self - end + def minutes + ActiveSupport::Duration.new(self * 60, [[:seconds, self * 60]]) + end + alias :minute :minutes + + def hours + ActiveSupport::Duration.new(self * 3600, [[:seconds, self * 3600]]) + end + alias :hour :hours + + def days + ActiveSupport::Duration.new(self * 24.hours, [[:days, self]]) + end + alias :day :days - # Reads best with argument: 10.minutes.until(time) - alias :until :ago + def weeks + ActiveSupport::Duration.new(self * 7.days, [[:days, self * 7]]) + end + alias :week :weeks + + def fortnights + ActiveSupport::Duration.new(self * 2.weeks, [[:days, self * 14]]) + end + alias :fortnight :fortnights + + # Reads best without arguments: 10.minutes.ago + def ago(time = ::Time.now) + time - self + end - # Reads best with argument: 10.minutes.since(time) - def since(time = ::Time.now) - time + self - end + # Reads best with argument: 10.minutes.until(time) + alias :until :ago - # Reads best without arguments: 10.minutes.from_now - alias :from_now :since - end - end + # Reads best with argument: 10.minutes.since(time) + def since(time = ::Time.now) + time + self end + + # Reads best without arguments: 10.minutes.from_now + alias :from_now :since end diff --git a/activesupport/lib/active_support/core_ext/object.rb b/activesupport/lib/active_support/core_ext/object.rb index 0796a7b710..96385d2b87 100644 --- a/activesupport/lib/active_support/core_ext/object.rb +++ b/activesupport/lib/active_support/core_ext/object.rb @@ -1,3 +1,7 @@ +require 'active_support/core_ext/object/blank' +require 'active_support/core_ext/object/duplicable' +require 'active_support/core_ext/object/try' + require 'active_support/core_ext/object/conversions' require 'active_support/core_ext/object/extending' require 'active_support/core_ext/object/instance_variables' diff --git a/activesupport/lib/active_support/core_ext/object/acts_like.rb b/activesupport/lib/active_support/core_ext/object/acts_like.rb new file mode 100644 index 0000000000..fcc8e50f06 --- /dev/null +++ b/activesupport/lib/active_support/core_ext/object/acts_like.rb @@ -0,0 +1,10 @@ +class Object + # A duck-type assistant method. For example, Active Support extends Date + # to define an acts_like_date? method, and extends Time to define + # acts_like_time?. As a result, we can do "x.acts_like?(:time)" and + # "x.acts_like?(:date)" to do duck-type-safe comparisons, since classes that + # we want to act like Time simply need to define an acts_like_time? method. + def acts_like?(duck) + respond_to? :"acts_like_#{duck}?" + end +end diff --git a/activesupport/lib/active_support/core_ext/blank.rb b/activesupport/lib/active_support/core_ext/object/blank.rb index 4f8dc4e281..9a1f663bf3 100644 --- a/activesupport/lib/active_support/core_ext/blank.rb +++ b/activesupport/lib/active_support/core_ext/object/blank.rb @@ -12,7 +12,7 @@ class Object def blank? respond_to?(:empty?) ? empty? : !self end - + # An object is present if it's not blank. def present? !blank? diff --git a/activesupport/lib/active_support/core_ext/duplicable.rb b/activesupport/lib/active_support/core_ext/object/duplicable.rb index 8f49ddfd9e..8f49ddfd9e 100644 --- a/activesupport/lib/active_support/core_ext/duplicable.rb +++ b/activesupport/lib/active_support/core_ext/object/duplicable.rb diff --git a/activesupport/lib/active_support/core_ext/object/misc.rb b/activesupport/lib/active_support/core_ext/object/misc.rb index 4acdfa3d6c..fb1bcdb98f 100644 --- a/activesupport/lib/active_support/core_ext/object/misc.rb +++ b/activesupport/lib/active_support/core_ext/object/misc.rb @@ -77,14 +77,4 @@ class Object def with_options(options) yield ActiveSupport::OptionMerger.new(self, options) end - - # A duck-type assistant method. For example, Active Support extends Date - # to define an acts_like_date? method, and extends Time to define - # acts_like_time?. As a result, we can do "x.acts_like?(:time)" and - # "x.acts_like?(:date)" to do duck-type-safe comparisons, since classes that - # we want to act like Time simply need to define an acts_like_time? method. - def acts_like?(duck) - respond_to? "acts_like_#{duck}?" - end - end diff --git a/activesupport/lib/active_support/core_ext/try.rb b/activesupport/lib/active_support/core_ext/object/try.rb index 3de198d198..a1c63a0e54 100644 --- a/activesupport/lib/active_support/core_ext/try.rb +++ b/activesupport/lib/active_support/core_ext/object/try.rb @@ -1,8 +1,8 @@ class Object - # Invokes the method identified by the symbol +method+, passing it any arguments + # Invokes the method identified by the symbol +method+, passing it any arguments # and/or the block specified, just like the regular Ruby <tt>Object#send</tt> does. # - # *Unlike* that method however, a +NoMethodError+ exception will *not* be raised + # *Unlike* that method however, a +NoMethodError+ exception will *not* be raised # and +nil+ will be returned instead, if the receiving object is a +nil+ object or NilClass. # # ==== Examples @@ -19,7 +19,7 @@ class Object # Person.try(:find, 1) # @people.try(:collect) {|p| p.name} #-- - # This method definition below is for rdoc purposes only. The alias_method call + # This method definition below is for rdoc purposes only. The alias_method call # below overrides it as an optimization since +try+ behaves like +Object#send+, # unless called on +NilClass+. def try(method, *args, &block) @@ -29,7 +29,7 @@ class Object alias_method :try, :__send__ end -class NilClass +class NilClass #:nodoc: def try(*args) nil end diff --git a/activesupport/lib/active_support/core_ext/pathname.rb b/activesupport/lib/active_support/core_ext/pathname.rb deleted file mode 100644 index 4c5318ee63..0000000000 --- a/activesupport/lib/active_support/core_ext/pathname.rb +++ /dev/null @@ -1,7 +0,0 @@ -require 'pathname' -require 'active_support/core_ext/pathname/clean_within' - -class Pathname#:nodoc: - extend ActiveSupport::CoreExtensions::Pathname::CleanWithin -end - diff --git a/activesupport/lib/active_support/core_ext/pathname/clean_within.rb b/activesupport/lib/active_support/core_ext/pathname/clean_within.rb deleted file mode 100644 index ae03e1bc5a..0000000000 --- a/activesupport/lib/active_support/core_ext/pathname/clean_within.rb +++ /dev/null @@ -1,14 +0,0 @@ -module ActiveSupport #:nodoc: - module CoreExtensions #:nodoc: - module Pathname #:nodoc: - module CleanWithin - # Clean the paths contained in the provided string. - def clean_within(string) - string.gsub(%r{[\w. ]+(/[\w. ]+)+(\.rb)?(\b|$)}) do |path| - new(path).cleanpath - end - end - end - end - end -end diff --git a/activesupport/lib/active_support/core_ext/process/daemon.rb b/activesupport/lib/active_support/core_ext/process/daemon.rb index 95ad5f8a5d..f5202ddee4 100644 --- a/activesupport/lib/active_support/core_ext/process/daemon.rb +++ b/activesupport/lib/active_support/core_ext/process/daemon.rb @@ -1,25 +1,23 @@ -if RUBY_VERSION < "1.9" - module Process - def self.daemon(nochdir = nil, noclose = nil) - exit if fork # Parent exits, child continues. - Process.setsid # Become session leader. - exit if fork # Zap session leader. See [1]. +module Process + def self.daemon(nochdir = nil, noclose = nil) + exit if fork # Parent exits, child continues. + Process.setsid # Become session leader. + exit if fork # Zap session leader. See [1]. - unless nochdir - Dir.chdir "/" # Release old working directory. - end + unless nochdir + Dir.chdir "/" # Release old working directory. + end - File.umask 0000 # Ensure sensible umask. Adjust as needed. + File.umask 0000 # Ensure sensible umask. Adjust as needed. - unless noclose - STDIN.reopen "/dev/null" # Free file descriptors and - STDOUT.reopen "/dev/null", "a" # point them somewhere sensible. - STDERR.reopen '/dev/null', 'a' - end + unless noclose + STDIN.reopen "/dev/null" # Free file descriptors and + STDOUT.reopen "/dev/null", "a" # point them somewhere sensible. + STDERR.reopen '/dev/null', 'a' + end - trap("TERM") { exit } + trap("TERM") { exit } - return 0 - end - end + return 0 + end unless respond_to?(:daemon) end diff --git a/activesupport/lib/active_support/core_ext/range.rb b/activesupport/lib/active_support/core_ext/range.rb index 0d2b169e3f..c0736f3a44 100644 --- a/activesupport/lib/active_support/core_ext/range.rb +++ b/activesupport/lib/active_support/core_ext/range.rb @@ -1,11 +1,4 @@ +require 'active_support/core_ext/range/blockless_step' require 'active_support/core_ext/range/conversions' -require 'active_support/core_ext/range/overlaps' require 'active_support/core_ext/range/include_range' -require 'active_support/core_ext/range/blockless_step' - -class Range #:nodoc: - include ActiveSupport::CoreExtensions::Range::Conversions - include ActiveSupport::CoreExtensions::Range::Overlaps - include ActiveSupport::CoreExtensions::Range::IncludeRange - include ActiveSupport::CoreExtensions::Range::BlocklessStep -end +require 'active_support/core_ext/range/overlaps' 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 6fa1eb5bee..f4792d03b7 100644 --- a/activesupport/lib/active_support/core_ext/range/blockless_step.rb +++ b/activesupport/lib/active_support/core_ext/range/blockless_step.rb @@ -1,32 +1,27 @@ -module ActiveSupport #:nodoc: - module CoreExtensions #:nodoc: - module Range #:nodoc: - # Return an array when step is called without a block. - module BlocklessStep - def self.included(base) #:nodoc: - base.alias_method_chain :step, :blockless - end - - if RUBY_VERSION < '1.9' - def step_with_blockless(value = 1, &block) - if block_given? - step_without_blockless(value, &block) - else - returning [] do |array| - step_without_blockless(value) { |step| array << step } - end - end - end - else - def step_with_blockless(value = 1, &block) - if block_given? - step_without_blockless(value, &block) - else - step_without_blockless(value).to_a - end - end - end +class Range + begin + (1..2).step + # Range#step doesn't return an Enumerator + rescue LocalJumpError + # Return an array when step is called without a block. + def step_with_blockless(*args, &block) + if block_given? + step_without_blockless(*args, &block) + else + array = [] + step_without_blockless(*args) { |step| array << step } + array + end + end + else + def step_with_blockless(*args, &block) #:nodoc: + if block_given? + step_without_blockless(*args, &block) + else + step_without_blockless(*args).to_a end end end + + alias_method_chain :step, :blockless end diff --git a/activesupport/lib/active_support/core_ext/range/conversions.rb b/activesupport/lib/active_support/core_ext/range/conversions.rb index 45b0826b62..11a7ff66de 100644 --- a/activesupport/lib/active_support/core_ext/range/conversions.rb +++ b/activesupport/lib/active_support/core_ext/range/conversions.rb @@ -1,27 +1,21 @@ -module ActiveSupport #:nodoc: - module CoreExtensions #:nodoc: - module Range #:nodoc: - # Getting ranges in different convenient string representations and other objects - module Conversions - RANGE_FORMATS = { - :db => Proc.new { |start, stop| "BETWEEN '#{start.to_s(:db)}' AND '#{stop.to_s(:db)}'" } - } +class Range + RANGE_FORMATS = { + :db => Proc.new { |start, stop| "BETWEEN '#{start.to_s(:db)}' AND '#{stop.to_s(:db)}'" } + } - def self.included(base) #:nodoc: - base.class_eval do - alias_method :to_default_s, :to_s - alias_method :to_s, :to_formatted_s - end - end - # Gives a human readable format of the range. - # - # ==== Example - # - # [1..100].to_formatted_s # => "1..100" - def to_formatted_s(format = :default) - RANGE_FORMATS[format] ? RANGE_FORMATS[format].call(first, last) : to_default_s - end - end + # Gives a human readable format of the range. + # + # ==== Example + # + # [1..100].to_formatted_s # => "1..100" + def to_formatted_s(format = :default) + if formatter = RANGE_FORMATS[format] + formatter.call(first, last) + else + to_default_s end end + + alias_method :to_default_s, :to_s + alias_method :to_s, :to_formatted_s end diff --git a/activesupport/lib/active_support/core_ext/range/include_range.rb b/activesupport/lib/active_support/core_ext/range/include_range.rb index 9a7d235695..0246627467 100644 --- a/activesupport/lib/active_support/core_ext/range/include_range.rb +++ b/activesupport/lib/active_support/core_ext/range/include_range.rb @@ -1,30 +1,21 @@ -module ActiveSupport #:nodoc: - module CoreExtensions #:nodoc: - module Range #:nodoc: - # Check if a Range includes another Range. - module IncludeRange - def self.included(base) #:nodoc: - base.alias_method_chain :include?, :range - end - - # Extends the default Range#include? to support range comparisons. - # (1..5).include?(1..5) # => true - # (1..5).include?(2..3) # => true - # (1..5).include?(2..6) # => false - # - # The native Range#include? behavior is untouched. - # ("a".."f").include?("c") # => true - # (5..9).include?(11) # => false - def include_with_range?(value) - if value.is_a?(::Range) - operator = exclude_end? ? :< : :<= - end_value = value.exclude_end? ? last.succ : last - include?(value.first) && (value.last <=> end_value).send(operator, 0) - else - include_without_range?(value) - end - end - end +class Range + # Extends the default Range#include? to support range comparisons. + # (1..5).include?(1..5) # => true + # (1..5).include?(2..3) # => true + # (1..5).include?(2..6) # => false + # + # The native Range#include? behavior is untouched. + # ("a".."f").include?("c") # => true + # (5..9).include?(11) # => false + def include_with_range?(value) + if value.is_a?(::Range) + operator = exclude_end? ? :< : :<= + end_value = value.exclude_end? ? last.succ : last + include_without_range?(value.first) && (value.last <=> end_value).send(operator, 0) + else + include_without_range?(value) end end + + alias_method_chain :include?, :range end diff --git a/activesupport/lib/active_support/core_ext/range/overlaps.rb b/activesupport/lib/active_support/core_ext/range/overlaps.rb index 43c69453e7..0dec6e0ac4 100644 --- a/activesupport/lib/active_support/core_ext/range/overlaps.rb +++ b/activesupport/lib/active_support/core_ext/range/overlaps.rb @@ -1,15 +1,8 @@ -module ActiveSupport #:nodoc: - module CoreExtensions #:nodoc: - module Range #:nodoc: - # Check if Ranges overlap. - module Overlaps - # Compare two ranges and see if they overlap eachother - # (1..5).overlaps?(4..6) # => true - # (1..5).overlaps?(7..9) # => false - def overlaps?(other) - include?(other.first) || other.include?(first) - end - end - end +class Range + # Compare two ranges and see if they overlap eachother + # (1..5).overlaps?(4..6) # => true + # (1..5).overlaps?(7..9) # => false + def overlaps?(other) + include?(other.first) || other.include?(first) end end diff --git a/activesupport/lib/active_support/core_ext/rexml.rb b/activesupport/lib/active_support/core_ext/rexml.rb index b4891a9153..decc4f6d9c 100644 --- a/activesupport/lib/active_support/core_ext/rexml.rb +++ b/activesupport/lib/active_support/core_ext/rexml.rb @@ -7,12 +7,12 @@ require 'rexml/rexml' unless (defined?(REXML::VERSION) ? REXML::VERSION : REXML::Version) > "3.1.7.2" require 'rexml/document' - # REXML in 1.8.7 has the patch but didn't update Version from 3.1.7.2. + # REXML in 1.8.7 has the patch but early patchlevels didn't update Version from 3.1.7.2. unless REXML::Document.respond_to?(:entity_expansion_limit=) require 'rexml/entity' - module REXML - class Entity < Child + module REXML #:nodoc: + class Entity < Child #:nodoc: undef_method :unnormalized def unnormalized document.record_entity_expansion! if document @@ -22,7 +22,7 @@ unless (defined?(REXML::VERSION) ? REXML::VERSION : REXML::Version) > "3.1.7.2" @unnormalized end end - class Document < Element + class Document < Element #:nodoc: @@entity_expansion_limit = 10_000 def self.entity_expansion_limit= val @@entity_expansion_limit = val diff --git a/activesupport/lib/active_support/core_ext/string.rb b/activesupport/lib/active_support/core_ext/string.rb index 16c544a577..222199a9a4 100644 --- a/activesupport/lib/active_support/core_ext/string.rb +++ b/activesupport/lib/active_support/core_ext/string.rb @@ -1,22 +1,19 @@ # encoding: utf-8 -require 'active_support/core_ext/string/inflections' require 'active_support/core_ext/string/conversions' -require 'active_support/core_ext/string/access' +require 'active_support/core_ext/string/filters' +require 'active_support/core_ext/string/multibyte' require 'active_support/core_ext/string/starts_ends_with' + +require 'active_support/core_ext/string/inflections' +require 'active_support/core_ext/string/access' require 'active_support/core_ext/string/iterators' -require 'active_support/core_ext/string/multibyte' 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 - include ActiveSupport::CoreExtensions::String::Conversions - include ActiveSupport::CoreExtensions::String::Filters include ActiveSupport::CoreExtensions::String::Inflections - include ActiveSupport::CoreExtensions::String::StartsEndsWith include ActiveSupport::CoreExtensions::String::Iterators include ActiveSupport::CoreExtensions::String::Behavior - include ActiveSupport::CoreExtensions::String::Multibyte end diff --git a/activesupport/lib/active_support/core_ext/string/conversions.rb b/activesupport/lib/active_support/core_ext/string/conversions.rb index d4334dcefe..39c2b1b8ed 100644 --- a/activesupport/lib/active_support/core_ext/string/conversions.rb +++ b/activesupport/lib/active_support/core_ext/string/conversions.rb @@ -1,28 +1,21 @@ require 'date' -module ActiveSupport #:nodoc: - module CoreExtensions #:nodoc: - module String #:nodoc: - # Converting strings to other objects - module Conversions - # 'a'.ord == 'a'[0] for Ruby 1.9 forward compatibility. - def ord - self[0] - end if RUBY_VERSION < '1.9' +class String + # 'a'.ord == 'a'[0] for Ruby 1.9 forward compatibility. + def ord + self[0] + end if RUBY_VERSION < '1.9' - # Form can be either :utc (default) or :local. - def to_time(form = :utc) - ::Time.send("#{form}_time", *::Date._parse(self, false).values_at(:year, :mon, :mday, :hour, :min, :sec).map { |arg| arg || 0 }) - end + # Form can be either :utc (default) or :local. + def to_time(form = :utc) + ::Time.send("#{form}_time", *::Date._parse(self, false).values_at(:year, :mon, :mday, :hour, :min, :sec).map { |arg| arg || 0 }) + end - def to_date - ::Date.new(*::Date._parse(self, false).values_at(:year, :mon, :mday)) - end + def to_date + ::Date.new(*::Date._parse(self, false).values_at(:year, :mon, :mday)) + end - def to_datetime - ::DateTime.civil(*::Date._parse(self, false).values_at(:year, :mon, :mday, :hour, :min, :sec).map { |arg| arg || 0 }) - end - end - end + def to_datetime + ::DateTime.civil(*::Date._parse(self, false).values_at(:year, :mon, :mday, :hour, :min, :sec).map { |arg| arg || 0 }) end end diff --git a/activesupport/lib/active_support/core_ext/string/filters.rb b/activesupport/lib/active_support/core_ext/string/filters.rb index 0329fbb8d4..6fda7efef5 100644 --- a/activesupport/lib/active_support/core_ext/string/filters.rb +++ b/activesupport/lib/active_support/core_ext/string/filters.rb @@ -1,26 +1,20 @@ -module ActiveSupport #:nodoc: - module CoreExtensions #:nodoc: - module String #:nodoc: - module Filters - # Returns the string, first removing all whitespace on both ends of - # the string, and then changing remaining consecutive whitespace - # groups into one space each. - # - # Examples: - # %{ Multi-line - # string }.squish # => "Multi-line string" - # " foo bar \n \t boo".squish # => "foo bar boo" - def squish - dup.squish! - end +class String + # Returns the string, first removing all whitespace on both ends of + # the string, and then changing remaining consecutive whitespace + # groups into one space each. + # + # Examples: + # %{ Multi-line + # string }.squish # => "Multi-line string" + # " foo bar \n \t boo".squish # => "foo bar boo" + def squish + dup.squish! + end - # Performs a destructive squish. See String#squish. - def squish! - strip! - gsub!(/\s+/, ' ') - self - end - end - end + # Performs a destructive squish. See String#squish. + def squish! + strip! + gsub!(/\s+/, ' ') + self end end diff --git a/activesupport/lib/active_support/core_ext/string/iterators.rb b/activesupport/lib/active_support/core_ext/string/iterators.rb index fe17d140ca..6ca6375fda 100644 --- a/activesupport/lib/active_support/core_ext/string/iterators.rb +++ b/activesupport/lib/active_support/core_ext/string/iterators.rb @@ -1,4 +1,4 @@ -require 'strscan' +autoload :StringScanner, 'strscan' unless defined? :StringScanner module ActiveSupport #:nodoc: module CoreExtensions #:nodoc: diff --git a/activesupport/lib/active_support/core_ext/string/multibyte.rb b/activesupport/lib/active_support/core_ext/string/multibyte.rb index 8f8f0968fd..0f0dfb2443 100644 --- a/activesupport/lib/active_support/core_ext/string/multibyte.rb +++ b/activesupport/lib/active_support/core_ext/string/multibyte.rb @@ -1,84 +1,73 @@ # encoding: utf-8 -module ActiveSupport #:nodoc: - module CoreExtensions #:nodoc: - module String #:nodoc: - # Implements multibyte methods for easier access to multibyte characters in a String instance. - module Multibyte - unless '1.9'.respond_to?(:force_encoding) - # == Multibyte proxy - # - # +mb_chars+ is a multibyte safe proxy for string methods. - # - # In Ruby 1.8 and older it creates and returns an instance of the ActiveSupport::Multibyte::Chars class which - # encapsulates the original string. A Unicode safe version of all the String methods are defined on this proxy - # class. If the proxy class doesn't respond to a certain method, it's forwarded to the encapsuled string. - # - # name = 'Claus Müller' - # name.reverse #=> "rell??M sualC" - # name.length #=> 13 - # - # name.mb_chars.reverse.to_s #=> "rellüM sualC" - # name.mb_chars.length #=> 12 - # - # In Ruby 1.9 and newer +mb_chars+ returns +self+ because String is (mostly) encoding aware. This means that - # it becomes easy to run one version of your code on multiple Ruby versions. - # - # == Method chaining - # - # All the methods on the Chars proxy which normally return a string will return a Chars object. This allows - # method chaining on the result of any of these methods. - # - # name.mb_chars.reverse.length #=> 12 - # - # == Interoperability and configuration - # - # The Chars object tries to be as interchangeable with String objects as possible: sorting and comparing between - # String and Char work like expected. The bang! methods change the internal string representation in the Chars - # object. Interoperability problems can be resolved easily with a +to_s+ call. - # - # For more information about the methods defined on the Chars proxy see ActiveSupport::Multibyte::Chars. For - # information about how to change the default Multibyte behaviour see ActiveSupport::Multibyte. - def mb_chars - if ActiveSupport::Multibyte.proxy_class.wants?(self) - ActiveSupport::Multibyte.proxy_class.new(self) - else - self - end - end - - # Returns true if the string has UTF-8 semantics (a String used for purely byte resources is unlikely to have - # them), returns false otherwise. - def is_utf8? - ActiveSupport::Multibyte::Chars.consumes?(self) - end +class String + unless '1.9'.respond_to?(:force_encoding) + # == Multibyte proxy + # + # +mb_chars+ is a multibyte safe proxy for string methods. + # + # In Ruby 1.8 and older it creates and returns an instance of the ActiveSupport::Multibyte::Chars class which + # encapsulates the original string. A Unicode safe version of all the String methods are defined on this proxy + # class. If the proxy class doesn't respond to a certain method, it's forwarded to the encapsuled string. + # + # name = 'Claus Müller' + # name.reverse #=> "rell??M sualC" + # name.length #=> 13 + # + # name.mb_chars.reverse.to_s #=> "rellüM sualC" + # name.mb_chars.length #=> 12 + # + # In Ruby 1.9 and newer +mb_chars+ returns +self+ because String is (mostly) encoding aware. This means that + # it becomes easy to run one version of your code on multiple Ruby versions. + # + # == Method chaining + # + # All the methods on the Chars proxy which normally return a string will return a Chars object. This allows + # method chaining on the result of any of these methods. + # + # name.mb_chars.reverse.length #=> 12 + # + # == Interoperability and configuration + # + # The Chars object tries to be as interchangeable with String objects as possible: sorting and comparing between + # String and Char work like expected. The bang! methods change the internal string representation in the Chars + # object. Interoperability problems can be resolved easily with a +to_s+ call. + # + # For more information about the methods defined on the Chars proxy see ActiveSupport::Multibyte::Chars. For + # information about how to change the default Multibyte behaviour see ActiveSupport::Multibyte. + def mb_chars + if ActiveSupport::Multibyte.proxy_class.wants?(self) + ActiveSupport::Multibyte.proxy_class.new(self) + else + self + end + end + + # Returns true if the string has UTF-8 semantics (a String used for purely byte resources is unlikely to have + # them), returns false otherwise. + def is_utf8? + ActiveSupport::Multibyte::Chars.consumes?(self) + end - unless '1.8.7 and later'.respond_to?(:chars) - def chars - # FIXME: - # ActiveSupport::Deprecation refers to RAILS_ENV - # and is a show stopper for 3rd party applications - # that only want ActiveSupport - ActiveSupport::Deprecation.warn('String#chars has been deprecated in favor of String#mb_chars.', caller) if defined?(ActiveSupport::Deprecation) - mb_chars - end - end - else - def mb_chars #:nodoc - self - end - - def is_utf8? #:nodoc - case encoding - when Encoding::UTF_8 - valid_encoding? - when Encoding::ASCII_8BIT, Encoding::US_ASCII - dup.force_encoding(Encoding::UTF_8).valid_encoding? - else - false - end - end - end + unless '1.8.7 and later'.respond_to?(:chars) + def chars + ActiveSupport::Deprecation.warn('String#chars has been deprecated in favor of String#mb_chars.', caller) + mb_chars + end + end + else + def mb_chars #:nodoc + self + end + + def is_utf8? #:nodoc + case encoding + when Encoding::UTF_8 + valid_encoding? + when Encoding::ASCII_8BIT, Encoding::US_ASCII + dup.force_encoding(Encoding::UTF_8).valid_encoding? + else + false end end end diff --git a/activesupport/lib/active_support/core_ext/string/starts_ends_with.rb b/activesupport/lib/active_support/core_ext/string/starts_ends_with.rb index 09f9a188b5..f65bb8f75b 100644 --- a/activesupport/lib/active_support/core_ext/string/starts_ends_with.rb +++ b/activesupport/lib/active_support/core_ext/string/starts_ends_with.rb @@ -1,35 +1,18 @@ -module ActiveSupport #:nodoc: - module CoreExtensions #:nodoc: - module String #:nodoc: - # Additional string tests. - module StartsEndsWith - def self.append_features(base) - if '1.8.7 and up'.respond_to?(:start_with?) - base.class_eval do - alias_method :starts_with?, :start_with? - alias_method :ends_with?, :end_with? - end - else - super - base.class_eval do - alias_method :start_with?, :starts_with? - alias_method :end_with?, :ends_with? - end - end - end - - # Does the string start with the specified +prefix+? - def starts_with?(prefix) - prefix = prefix.to_s - self[0, prefix.length] == prefix - end +class String + unless '1.8.7 and up'.respond_to?(:start_with?) + # Does the string start with the specified +prefix+? + def start_with?(prefix) + prefix = prefix.to_s + self[0, prefix.length] == prefix + end - # Does the string end with the specified +suffix+? - def ends_with?(suffix) - suffix = suffix.to_s - self[-suffix.length, suffix.length] == suffix - end - end + # Does the string end with the specified +suffix+? + def end_with?(suffix) + suffix = suffix.to_s + self[-suffix.length, suffix.length] == suffix end end + + alias_method :starts_with?, :start_with? + alias_method :ends_with?, :end_with? end diff --git a/activesupport/lib/active_support/core_ext/string/xchar.rb b/activesupport/lib/active_support/core_ext/string/xchar.rb index df186e42d7..7183218634 100644 --- a/activesupport/lib/active_support/core_ext/string/xchar.rb +++ b/activesupport/lib/active_support/core_ext/string/xchar.rb @@ -1,11 +1,18 @@ begin - # See http://bogomips.org/fast_xs/ by Eric Wong + # See http://bogomips.org/fast_xs/ by Eric Wong. + # Also included with hpricot. require 'fast_xs' +rescue LoadError + # fast_xs extension unavailable +else + begin + require 'builder' + rescue LoadError + # builder demands the first shot at defining String#to_xs + end class String alias_method :original_xs, :to_xs if method_defined?(:to_xs) alias_method :to_xs, :fast_xs end -rescue LoadError - # fast_xs extension unavailable. end diff --git a/activesupport/lib/active_support/core_ext/symbol.rb b/activesupport/lib/active_support/core_ext/symbol.rb index e4ac443809..520369452b 100644 --- a/activesupport/lib/active_support/core_ext/symbol.rb +++ b/activesupport/lib/active_support/core_ext/symbol.rb @@ -1,14 +1,14 @@ -unless :to_proc.respond_to?(:to_proc) - class Symbol - # Turns the symbol into a simple proc, which is especially useful for enumerations. Examples: - # - # # The same as people.collect { |p| p.name } - # people.collect(&:name) - # - # # The same as people.select { |p| p.manager? }.collect { |p| p.salary } - # people.select(&:manager?).collect(&:salary) - def to_proc - Proc.new { |*args| args.shift.__send__(self, *args) } - end - end +class Symbol + # Turns the symbol into a simple proc, which is especially useful for enumerations. Examples: + # + # # The same as people.collect { |p| p.name } + # people.collect(&:name) + # + # # The same as people.select { |p| p.manager? }.collect { |p| p.salary } + # people.select(&:manager?).collect(&:salary) + # + # This is a builtin method in Ruby 1.8.7 and later. + def to_proc + Proc.new { |*args| args.shift.__send__(self, *args) } + end unless :to_proc.respond_to?(:to_proc) end diff --git a/activesupport/lib/active_support/core_ext/time.rb b/activesupport/lib/active_support/core_ext/time.rb index 78bbfc917c..b28f7f1a32 100644 --- a/activesupport/lib/active_support/core_ext/time.rb +++ b/activesupport/lib/active_support/core_ext/time.rb @@ -1,42 +1,10 @@ require 'date' require 'time' -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 +require 'active_support/core_ext/time/publicize_conversion_methods' +require 'active_support/core_ext/time/marshal_with_utc_flag' - # 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' +require 'active_support/core_ext/time/acts_like' require 'active_support/core_ext/time/calculations' require 'active_support/core_ext/time/conversions' require 'active_support/core_ext/time/zones' - -class Time#:nodoc: - include ActiveSupport::CoreExtensions::Time::Behavior - include ActiveSupport::CoreExtensions::Time::Calculations - include ActiveSupport::CoreExtensions::Time::Conversions - include ActiveSupport::CoreExtensions::Time::Zones -end diff --git a/activesupport/lib/active_support/core_ext/time/acts_like.rb b/activesupport/lib/active_support/core_ext/time/acts_like.rb new file mode 100644 index 0000000000..3f853b7893 --- /dev/null +++ b/activesupport/lib/active_support/core_ext/time/acts_like.rb @@ -0,0 +1,8 @@ +require 'active_support/core_ext/object/acts_like' + +class Time + # Duck-types as a Time-like class. See Object#acts_like?. + def acts_like_time? + true + end +end diff --git a/activesupport/lib/active_support/core_ext/time/behavior.rb b/activesupport/lib/active_support/core_ext/time/behavior.rb deleted file mode 100644 index a5c0baacdf..0000000000 --- a/activesupport/lib/active_support/core_ext/time/behavior.rb +++ /dev/null @@ -1,13 +0,0 @@ -module ActiveSupport #:nodoc: - module CoreExtensions #:nodoc: - module Time #:nodoc: - module Behavior - # Enable more predictable duck-typing on Time-like classes. See - # Object#acts_like?. - def acts_like_time? - true - end - end - end - end -end diff --git a/activesupport/lib/active_support/core_ext/time/calculations.rb b/activesupport/lib/active_support/core_ext/time/calculations.rb index d13d0e01a7..b73c3b2c9b 100644 --- a/activesupport/lib/active_support/core_ext/time/calculations.rb +++ b/activesupport/lib/active_support/core_ext/time/calculations.rb @@ -1,295 +1,279 @@ require 'active_support/duration' +require 'active_support/core_ext/date/calculations' -module ActiveSupport #:nodoc: - module CoreExtensions #:nodoc: - module Time #:nodoc: - # Enables the use of time calculations within Time itself - module Calculations - def self.included(base) #:nodoc: - base.extend ClassMethods - - 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 - end - - COMMON_YEAR_DAYS_IN_MONTH = [nil, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31] - - module ClassMethods - # Overriding case equality method so that it returns true for ActiveSupport::TimeWithZone instances - 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. - def days_in_month(month, year = now.year) - return 29 if month == 2 && ::Date.gregorian_leap?(year) - COMMON_YEAR_DAYS_IN_MONTH[month] - end - - # Returns a new Time if requested year can be accommodated by Ruby's Time class - # (i.e., if year is within either 1970..2038 or 1902..2038, depending on system architecture); - # otherwise returns a DateTime - def time_with_datetime_fallback(utc_or_local, year, month=1, day=1, hour=0, min=0, sec=0, usec=0) - ::Time.send(utc_or_local, year, month, day, hour, min, sec, usec) - rescue - offset = utc_or_local.to_sym == :local ? ::DateTime.local_offset : 0 - ::DateTime.civil(year, month, day, hour, min, sec, offset) - end - - # Wraps class method +time_with_datetime_fallback+ with +utc_or_local+ set to <tt>:utc</tt>. - def utc_time(*args) - time_with_datetime_fallback(:utc, *args) - end - - # Wraps class method +time_with_datetime_fallback+ with +utc_or_local+ set to <tt>:local</tt>. - def local_time(*args) - time_with_datetime_fallback(:local, *args) - 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) - end - - # Returns a new Time where one or more of the elements have been changed according to the +options+ parameter. The time options - # (hour, minute, sec, usec) reset cascadingly, so if only the hour is passed, then minute, sec, and usec is set to 0. If the hour and - # minute is passed, then sec and usec is set to 0. - def change(options) - ::Time.send( - self.utc? ? :utc_time : :local_time, - options[:year] || self.year, - options[:month] || self.month, - options[:day] || self.day, - options[:hour] || self.hour, - options[:min] || (options[:hour] ? 0 : self.min), - options[:sec] || ((options[:hour] || options[:min]) ? 0 : self.sec), - options[:usec] || ((options[:hour] || options[:min] || options[:sec]) ? 0 : self.usec) - ) - end - - # Uses Date to provide precise Time 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>, <tt>:hours</tt>, - # <tt>:minutes</tt>, <tt>:seconds</tt>. - def advance(options) - unless options[:weeks].nil? - options[:weeks], partial_weeks = options[:weeks].divmod(1) - options[:days] = (options[:days] || 0) + 7 * partial_weeks - end - - unless options[:days].nil? - options[:days], partial_days = options[:days].divmod(1) - options[:hours] = (options[:hours] || 0) + 24 * partial_days - end - - d = to_date.advance(options) - time_advanced_by_date = change(:year => d.year, :month => d.month, :day => d.day) - seconds_to_advance = (options[:seconds] || 0) + (options[:minutes] || 0) * 60 + (options[:hours] || 0) * 3600 - seconds_to_advance == 0 ? time_advanced_by_date : time_advanced_by_date.since(seconds_to_advance) - end - - # Returns a new Time representing the time a number of seconds ago - def ago(seconds) - self.since(-seconds) - end - - # Returns a new Time representing the time a number of seconds since the instance time - def since(seconds) - self + seconds - rescue - self.to_datetime.since(seconds) - end - alias :in :since - - # Returns a new Time representing the time a number of specified months ago - def months_ago(months) - advance(:months => -months) - end - - # Returns a new Time representing the time a number of specified months in the future - def months_since(months) - advance(:months => months) - end - - # Returns a new Time representing the time a number of specified years ago - def years_ago(years) - advance(:years => -years) - end - - # Returns a new Time representing the time a number of specified years in the future - def years_since(years) - advance(:years => years) - end - - # Short-hand for years_ago(1) - def last_year - years_ago(1) - end - - # Short-hand for years_since(1) - def next_year - years_since(1) - end - - - # Short-hand for months_ago(1) - def last_month - months_ago(1) - end - - # Short-hand for months_since(1) - def next_month - months_since(1) - end - - # Returns a new Time representing the "start" of this week (Monday, 0:00) - def beginning_of_week - days_to_monday = self.wday!=0 ? self.wday-1 : 6 - (self - days_to_monday.days).midnight - end - alias :monday :beginning_of_week - alias :at_beginning_of_week :beginning_of_week - - # Returns a new Time representing the end of this week (Sunday, 23:59:59) - def end_of_week - days_to_sunday = self.wday!=0 ? 7-self.wday : 0 - (self + days_to_sunday.days).end_of_day - end - alias :at_end_of_week :end_of_week - - # Returns a new Time representing the start of the given day in next week (default is Monday). - def next_week(day = :monday) - days_into_week = { :monday => 0, :tuesday => 1, :wednesday => 2, :thursday => 3, :friday => 4, :saturday => 5, :sunday => 6} - since(1.week).beginning_of_week.since(days_into_week[day].day).change(:hour => 0) - end - - # Returns a new Time representing the start of the day (0:00) - def beginning_of_day - (self - self.seconds_since_midnight).change(:usec => 0) - end - alias :midnight :beginning_of_day - alias :at_midnight :beginning_of_day - alias :at_beginning_of_day :beginning_of_day - - # Returns a new Time representing the end of the day (23:59:59) - def end_of_day - change(:hour => 23, :min => 59, :sec => 59) - end - - # Returns a new Time representing the start of the month (1st of the month, 0:00) - def beginning_of_month - #self - ((self.mday-1).days + self.seconds_since_midnight) - change(:day => 1,:hour => 0, :min => 0, :sec => 0, :usec => 0) - end - alias :at_beginning_of_month :beginning_of_month - - # Returns a new Time representing the end of the month (last day of the month, 0:00) - def end_of_month - #self - ((self.mday-1).days + self.seconds_since_midnight) - last_day = ::Time.days_in_month( self.month, self.year ) - 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 }) - end - alias :at_beginning_of_quarter :beginning_of_quarter - - # Returns a new Time representing the end of the quarter (last day of march, june, september, december, 23:59:59) - def end_of_quarter - 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 - - # Returns a new Time representing the start of the year (1st of january, 0:00) - def beginning_of_year - change(:month => 1,:day => 1,:hour => 0, :min => 0, :sec => 0, :usec => 0) - end - alias :at_beginning_of_year :beginning_of_year - - # Returns a new Time representing the end of the year (31st of december, 23:59:59) - def end_of_year - change(:month => 12,:day => 31,:hour => 23, :min => 59, :sec => 59) - end - alias :at_end_of_year :end_of_year - - # Convenience method which returns a new Time representing the time 1 day ago - def yesterday - advance(:days => -1) - end - - # Convenience method which returns a new Time representing the time 1 day since the instance time - def tomorrow - advance(:days => 1) - end - - def plus_with_duration(other) #:nodoc: - if ActiveSupport::Duration === other - other.since(self) - else - plus_without_duration(other) - end - end - - def minus_with_duration(other) #:nodoc: - if ActiveSupport::Duration === other - other.until(self) - else - 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 - def minus_with_coercion(other) - 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) - # if other is an ActiveSupport::TimeWithZone, coerce a Time instance from it so we can do <=> comparison - other = other.comparable_time if other.respond_to?(:comparable_time) - if other.acts_like?(:date) - # other is a Date/DateTime, so coerce self #to_datetime and hand off to DateTime#<=> - to_datetime.compare_without_coercion(other) - else - compare_without_coercion(other) - end - end - end +class Time + COMMON_YEAR_DAYS_IN_MONTH = [nil, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31] + DAYS_INTO_WEEK = { :monday => 0, :tuesday => 1, :wednesday => 2, :thursday => 3, :friday => 4, :saturday => 5, :sunday => 6 } + + class << self + # Overriding case equality method so that it returns true for ActiveSupport::TimeWithZone instances + 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. + def days_in_month(month, year = now.year) + return 29 if month == 2 && ::Date.gregorian_leap?(year) + COMMON_YEAR_DAYS_IN_MONTH[month] + end + + # Returns a new Time if requested year can be accommodated by Ruby's Time class + # (i.e., if year is within either 1970..2038 or 1902..2038, depending on system architecture); + # otherwise returns a DateTime + def time_with_datetime_fallback(utc_or_local, year, month=1, day=1, hour=0, min=0, sec=0, usec=0) + ::Time.send(utc_or_local, year, month, day, hour, min, sec, usec) + rescue + offset = utc_or_local.to_sym == :local ? ::DateTime.local_offset : 0 + ::DateTime.civil(year, month, day, hour, min, sec, offset) + end + + # Wraps class method +time_with_datetime_fallback+ with +utc_or_local+ set to <tt>:utc</tt>. + def utc_time(*args) + time_with_datetime_fallback(:utc, *args) + end + + # Wraps class method +time_with_datetime_fallback+ with +utc_or_local+ set to <tt>:local</tt>. + def local_time(*args) + time_with_datetime_fallback(:local, *args) + end + end + + # Tells whether the Time object's time lies in the past + def past? + self < ::Time.current + end + + # Tells whether the Time object's time is today + def today? + to_date == ::Date.current + end + + # Tells whether the Time object's time lies in the future + def future? + self > ::Time.current + end + + # Seconds since midnight: Time.now.seconds_since_midnight + def seconds_since_midnight + to_i - change(:hour => 0).to_i + (usec / 1.0e+6) + end + + # Returns a new Time where one or more of the elements have been changed according to the +options+ parameter. The time options + # (hour, minute, sec, usec) reset cascadingly, so if only the hour is passed, then minute, sec, and usec is set to 0. If the hour and + # minute is passed, then sec and usec is set to 0. + def change(options) + ::Time.send( + utc? ? :utc_time : :local_time, + options[:year] || year, + options[:month] || month, + options[:day] || day, + options[:hour] || hour, + options[:min] || (options[:hour] ? 0 : min), + options[:sec] || ((options[:hour] || options[:min]) ? 0 : sec), + options[:usec] || ((options[:hour] || options[:min] || options[:sec]) ? 0 : usec) + ) + end + + # Uses Date to provide precise Time 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>, <tt>:hours</tt>, + # <tt>:minutes</tt>, <tt>:seconds</tt>. + def advance(options) + unless options[:weeks].nil? + options[:weeks], partial_weeks = options[:weeks].divmod(1) + options[:days] = (options[:days] || 0) + 7 * partial_weeks + end + + unless options[:days].nil? + options[:days], partial_days = options[:days].divmod(1) + options[:hours] = (options[:hours] || 0) + 24 * partial_days + end + + d = to_date.advance(options) + time_advanced_by_date = change(:year => d.year, :month => d.month, :day => d.day) + seconds_to_advance = (options[:seconds] || 0) + (options[:minutes] || 0) * 60 + (options[:hours] || 0) * 3600 + seconds_to_advance == 0 ? time_advanced_by_date : time_advanced_by_date.since(seconds_to_advance) + end + + # Returns a new Time representing the time a number of seconds ago, this is basically a wrapper around the Numeric extension + def ago(seconds) + since(-seconds) + end + + # Returns a new Time representing the time a number of seconds since the instance time + def since(seconds) + self + seconds + rescue + to_datetime.since(seconds) + end + alias :in :since + + # Returns a new Time representing the time a number of specified months ago + def months_ago(months) + advance(:months => -months) + end + + # Returns a new Time representing the time a number of specified months in the future + def months_since(months) + advance(:months => months) + end + + # Returns a new Time representing the time a number of specified years ago + def years_ago(years) + advance(:years => -years) + end + + # Returns a new Time representing the time a number of specified years in the future + def years_since(years) + advance(:years => years) + end + + # Short-hand for years_ago(1) + def last_year + years_ago(1) + end + + # Short-hand for years_since(1) + def next_year + years_since(1) + end + + + # Short-hand for months_ago(1) + def last_month + months_ago(1) + end + + # Short-hand for months_since(1) + def next_month + months_since(1) + end + + # Returns a new Time representing the "start" of this week (Monday, 0:00) + def beginning_of_week + days_to_monday = wday!=0 ? wday-1 : 6 + (self - days_to_monday.days).midnight + end + alias :monday :beginning_of_week + alias :at_beginning_of_week :beginning_of_week + + # Returns a new Time representing the end of this week (Sunday, 23:59:59) + def end_of_week + days_to_sunday = wday!=0 ? 7-wday : 0 + (self + days_to_sunday.days).end_of_day + end + alias :at_end_of_week :end_of_week + + # Returns a new Time representing the start of the given day in next week (default is Monday). + def next_week(day = :monday) + since(1.week).beginning_of_week.since(DAYS_INTO_WEEK[day].day).change(:hour => 0) + end + + # Returns a new Time representing the start of the day (0:00) + def beginning_of_day + (self - seconds_since_midnight).change(:usec => 0) + end + alias :midnight :beginning_of_day + alias :at_midnight :beginning_of_day + alias :at_beginning_of_day :beginning_of_day + + # Returns a new Time representing the end of the day (23:59:59) + def end_of_day + change(:hour => 23, :min => 59, :sec => 59) + end + + # Returns a new Time representing the start of the month (1st of the month, 0:00) + def beginning_of_month + #self - ((self.mday-1).days + self.seconds_since_midnight) + change(:day => 1,:hour => 0, :min => 0, :sec => 0, :usec => 0) + end + alias :at_beginning_of_month :beginning_of_month + + # Returns a new Time representing the end of the month (last day of the month, 0:00) + def end_of_month + #self - ((self.mday-1).days + self.seconds_since_midnight) + last_day = ::Time.days_in_month(month, year) + change(:day => last_day, :hour => 23, :min => 59, :sec => 59, :usec => 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 <= month }) + end + alias :at_beginning_of_quarter :beginning_of_quarter + + # Returns a new Time representing the end of the quarter (last day of march, june, september, december, 23:59:59) + def end_of_quarter + beginning_of_month.change(:month => [3, 6, 9, 12].detect { |m| m >= month }).end_of_month + end + alias :at_end_of_quarter :end_of_quarter + + # Returns a new Time representing the start of the year (1st of january, 0:00) + def beginning_of_year + change(:month => 1, :day => 1, :hour => 0, :min => 0, :sec => 0, :usec => 0) + end + alias :at_beginning_of_year :beginning_of_year + + # Returns a new Time representing the end of the year (31st of december, 23:59:59) + def end_of_year + change(:month => 12, :day => 31, :hour => 23, :min => 59, :sec => 59) + end + alias :at_end_of_year :end_of_year + + # Convenience method which returns a new Time representing the time 1 day ago + def yesterday + advance(:days => -1) + end + + # Convenience method which returns a new Time representing the time 1 day since the instance time + def tomorrow + advance(:days => 1) + end + + def plus_with_duration(other) #:nodoc: + if ActiveSupport::Duration === other + other.since(self) + else + plus_without_duration(other) + end + end + alias_method :plus_without_duration, :+ + alias_method :+, :plus_with_duration + + def minus_with_duration(other) #:nodoc: + if ActiveSupport::Duration === other + other.until(self) + else + minus_without_duration(other) + end + end + alias_method :minus_without_duration, :- + alias_method :-, :minus_with_duration + + # 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 + def minus_with_coercion(other) + other = other.comparable_time if other.respond_to?(:comparable_time) + minus_without_coercion(other) + end + alias_method :minus_without_coercion, :- + alias_method :-, :minus_with_coercion + + # Layers additional behavior on Time#<=> so that DateTime and ActiveSupport::TimeWithZone instances + # can be chronologically compared with a Time + def compare_with_coercion(other) + # if other is an ActiveSupport::TimeWithZone, coerce a Time instance from it so we can do <=> comparison + other = other.comparable_time if other.respond_to?(:comparable_time) + if other.acts_like?(:date) + # other is a Date/DateTime, so coerce self #to_datetime and hand off to DateTime#<=> + to_datetime.compare_without_coercion(other) + else + compare_without_coercion(other) end end + alias_method :compare_without_coercion, :<=> + alias_method :<=>, :compare_with_coercion end diff --git a/activesupport/lib/active_support/core_ext/time/conversions.rb b/activesupport/lib/active_support/core_ext/time/conversions.rb index e6f9134661..18261e139f 100644 --- a/activesupport/lib/active_support/core_ext/time/conversions.rb +++ b/activesupport/lib/active_support/core_ext/time/conversions.rb @@ -1,90 +1,84 @@ -module ActiveSupport #:nodoc: - module CoreExtensions #:nodoc: - module Time #:nodoc: - # Converting times to formatted strings, dates, and datetimes. - module Conversions - DATE_FORMATS = { - :db => "%Y-%m-%d %H:%M:%S", - :number => "%Y%m%d%H%M%S", - :time => "%H:%M", - :short => "%d %b %H:%M", - :long => "%B %d, %Y %H:%M", - :long_ordinal => lambda { |time| time.strftime("%B #{time.day.ordinalize}, %Y %H:%M") }, - :rfc822 => lambda { |time| time.strftime("%a, %d %b %Y %H:%M:%S #{time.formatted_offset(false)}") } - } +require 'active_support/inflector' +require 'active_support/values/time_zone' - def self.included(base) #:nodoc: - base.class_eval do - alias_method :to_default_s, :to_s - alias_method :to_s, :to_formatted_s - end - end +class Time + DATE_FORMATS = { + :db => "%Y-%m-%d %H:%M:%S", + :number => "%Y%m%d%H%M%S", + :time => "%H:%M", + :short => "%d %b %H:%M", + :long => "%B %d, %Y %H:%M", + :long_ordinal => lambda { |time| time.strftime("%B #{ActiveSupport::Inflector.ordinalize(time.day)}, %Y %H:%M") }, + :rfc822 => lambda { |time| time.strftime("%a, %d %b %Y %H:%M:%S #{time.formatted_offset(false)}") } + } - # Converts to a formatted string. See DATE_FORMATS for builtin formats. - # - # This method is aliased to <tt>to_s</tt>. - # - # time = Time.now # => Thu Jan 18 06:10:17 CST 2007 - # - # time.to_formatted_s(:time) # => "06:10:17" - # time.to_s(:time) # => "06:10:17" - # - # time.to_formatted_s(:db) # => "2007-01-18 06:10:17" - # time.to_formatted_s(:number) # => "20070118061017" - # time.to_formatted_s(:short) # => "18 Jan 06:10" - # time.to_formatted_s(:long) # => "January 18, 2007 06:10" - # time.to_formatted_s(:long_ordinal) # => "January 18th, 2007 06:10" - # time.to_formatted_s(:rfc822) # => "Thu, 18 Jan 2007 06:10:17 -0600" - # - # == Adding your own time formats to +to_formatted_s+ - # You can add your own formats to the Time::DATE_FORMATS hash. - # Use the format name as the hash key and either a strftime string - # or Proc instance that takes a time argument as the value. - # - # # config/initializers/time_formats.rb - # Time::DATE_FORMATS[:month_and_year] = "%B %Y" - # Time::DATE_FORMATS[:short_ordinal] = lambda { |time| time.strftime("%B #{time.day.ordinalize}") } - def to_formatted_s(format = :default) - return to_default_s unless formatter = DATE_FORMATS[format] - formatter.respond_to?(:call) ? formatter.call(self).to_s : strftime(formatter) - end - - # Returns the UTC offset as an +HH:MM formatted string. - # - # Time.local(2000).formatted_offset # => "-06:00" - # Time.local(2000).formatted_offset(false) # => "-0600" - def formatted_offset(colon = true, alternate_utc_string = nil) - utc? && alternate_utc_string || utc_offset.to_utc_offset_s(colon) - end + # Converts to a formatted string. See DATE_FORMATS for builtin formats. + # + # This method is aliased to <tt>to_s</tt>. + # + # time = Time.now # => Thu Jan 18 06:10:17 CST 2007 + # + # time.to_formatted_s(:time) # => "06:10:17" + # time.to_s(:time) # => "06:10:17" + # + # time.to_formatted_s(:db) # => "2007-01-18 06:10:17" + # time.to_formatted_s(:number) # => "20070118061017" + # time.to_formatted_s(:short) # => "18 Jan 06:10" + # time.to_formatted_s(:long) # => "January 18, 2007 06:10" + # time.to_formatted_s(:long_ordinal) # => "January 18th, 2007 06:10" + # time.to_formatted_s(:rfc822) # => "Thu, 18 Jan 2007 06:10:17 -0600" + # + # == Adding your own time formats to +to_formatted_s+ + # You can add your own formats to the Time::DATE_FORMATS hash. + # Use the format name as the hash key and either a strftime string + # or Proc instance that takes a time argument as the value. + # + # # config/initializers/time_formats.rb + # Time::DATE_FORMATS[:month_and_year] = "%B %Y" + # Time::DATE_FORMATS[:short_ordinal] = lambda { |time| time.strftime("%B #{time.day.ordinalize}") } + def to_formatted_s(format = :default) + if formatter = DATE_FORMATS[format] + formatter.respond_to?(:call) ? formatter.call(self).to_s : strftime(formatter) + else + to_default_s + end + end + alias_method :to_default_s, :to_s + alias_method :to_s, :to_formatted_s + + # Returns the UTC offset as an +HH:MM formatted string. + # + # Time.local(2000).formatted_offset # => "-06:00" + # Time.local(2000).formatted_offset(false) # => "-0600" + def formatted_offset(colon = true, alternate_utc_string = nil) + utc? && alternate_utc_string || ActiveSupport::TimeZone.seconds_to_utc_offset(utc_offset, colon) + end - # Converts a Time object to a Date, dropping hour, minute, and second precision. - # - # my_time = Time.now # => Mon Nov 12 22:59:51 -0500 2007 - # my_time.to_date # => Mon, 12 Nov 2007 - # - # your_time = Time.parse("1/13/2009 1:13:03 P.M.") # => Tue Jan 13 13:13:03 -0500 2009 - # your_time.to_date # => Tue, 13 Jan 2009 - def to_date - ::Date.new(year, month, day) - end + # Converts a Time object to a Date, dropping hour, minute, and second precision. + # + # my_time = Time.now # => Mon Nov 12 22:59:51 -0500 2007 + # my_time.to_date # => Mon, 12 Nov 2007 + # + # your_time = Time.parse("1/13/2009 1:13:03 P.M.") # => Tue Jan 13 13:13:03 -0500 2009 + # your_time.to_date # => Tue, 13 Jan 2009 + def to_date + ::Date.new(year, month, day) + end - # A method to keep Time, Date and DateTime instances interchangeable on conversions. - # In this case, it simply returns +self+. - def to_time - self - end + # A method to keep Time, Date and DateTime instances interchangeable on conversions. + # In this case, it simply returns +self+. + def to_time + self + end - # Converts a Time instance to a Ruby DateTime instance, preserving UTC offset. - # - # my_time = Time.now # => Mon Nov 12 23:04:21 -0500 2007 - # my_time.to_datetime # => Mon, 12 Nov 2007 23:04:21 -0500 - # - # your_time = Time.parse("1/13/2009 1:13:03 P.M.") # => Tue Jan 13 13:13:03 -0500 2009 - # your_time.to_datetime # => Tue, 13 Jan 2009 13:13:03 -0500 - def to_datetime - ::DateTime.civil(year, month, day, hour, min, sec, Rational(utc_offset, 86400)) - end - end - end + # Converts a Time instance to a Ruby DateTime instance, preserving UTC offset. + # + # my_time = Time.now # => Mon Nov 12 23:04:21 -0500 2007 + # my_time.to_datetime # => Mon, 12 Nov 2007 23:04:21 -0500 + # + # your_time = Time.parse("1/13/2009 1:13:03 P.M.") # => Tue Jan 13 13:13:03 -0500 2009 + # your_time.to_datetime # => Tue, 13 Jan 2009 13:13:03 -0500 + def to_datetime + ::DateTime.civil(year, month, day, hour, min, sec, Rational(utc_offset, 86400)) end end diff --git a/activesupport/lib/active_support/core_ext/time/marshal_with_utc_flag.rb b/activesupport/lib/active_support/core_ext/time/marshal_with_utc_flag.rb new file mode 100644 index 0000000000..a1c8ece1d7 --- /dev/null +++ b/activesupport/lib/active_support/core_ext/time/marshal_with_utc_flag.rb @@ -0,0 +1,22 @@ +# 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 Time + 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 = frozen? ? dup : self + obj.instance_variable_set('@marshal_with_utc_coercion', utc?) + obj._original_dump(*args) + end + end +end diff --git a/activesupport/lib/active_support/core_ext/time/publicize_conversion_methods.rb b/activesupport/lib/active_support/core_ext/time/publicize_conversion_methods.rb new file mode 100644 index 0000000000..e1878d3c20 --- /dev/null +++ b/activesupport/lib/active_support/core_ext/time/publicize_conversion_methods.rb @@ -0,0 +1,10 @@ +require 'date' + +class Time + # Ruby 1.8-cvs and early 1.9 series define private Time#to_date + %w(to_date to_datetime).each do |method| + if private_instance_methods.include?(method) || private_instance_methods.include?(method.to_sym) + public method + end + end +end diff --git a/activesupport/lib/active_support/core_ext/time/zones.rb b/activesupport/lib/active_support/core_ext/time/zones.rb index 9d8eb73908..adc9fe3824 100644 --- a/activesupport/lib/active_support/core_ext/time/zones.rb +++ b/activesupport/lib/active_support/core_ext/time/zones.rb @@ -1,86 +1,78 @@ -module ActiveSupport #:nodoc: - module CoreExtensions #:nodoc: - module Time #:nodoc: - module Zones - def self.included(base) #:nodoc: - base.extend(ClassMethods) if base == ::Time # i.e., don't include class methods in DateTime - end - - module ClassMethods - attr_accessor :zone_default - - # Returns the TimeZone for the current request, if this has been set (via Time.zone=). - # If <tt>Time.zone</tt> has not been set for the current request, returns the TimeZone specified in <tt>config.time_zone</tt>. - def zone - Thread.current[:time_zone] || zone_default - end +require 'active_support/time_with_zone' + +class Time + class << self + attr_accessor :zone_default + + # Returns the TimeZone for the current request, if this has been set (via Time.zone=). + # If <tt>Time.zone</tt> has not been set for the current request, returns the TimeZone specified in <tt>config.time_zone</tt>. + def zone + Thread.current[:time_zone] || zone_default + end - # Sets <tt>Time.zone</tt> to a TimeZone object for the current request/thread. - # - # This method accepts any of the following: - # - # * A Rails TimeZone object. - # * An identifier for a Rails TimeZone object (e.g., "Eastern Time (US & Canada)", <tt>-5.hours</tt>). - # * A TZInfo::Timezone object. - # * An identifier for a TZInfo::Timezone object (e.g., "America/New_York"). - # - # Here's an example of how you might set <tt>Time.zone</tt> on a per request basis -- <tt>current_user.time_zone</tt> - # just needs to return a string identifying the user's preferred TimeZone: - # - # class ApplicationController < ActionController::Base - # before_filter :set_time_zone - # - # def set_time_zone - # Time.zone = current_user.time_zone - # end - # end - def zone=(time_zone) - Thread.current[:time_zone] = get_zone(time_zone) - end - - # Allows override of <tt>Time.zone</tt> locally inside supplied block; resets <tt>Time.zone</tt> to existing value when done. - def use_zone(time_zone) - old_zone, ::Time.zone = ::Time.zone, get_zone(time_zone) - yield - ensure - ::Time.zone = old_zone - end - - # Returns <tt>Time.zone.now</tt> when <tt>config.time_zone</tt> is set, otherwise just returns <tt>Time.now</tt>. - def current - ::Time.zone_default ? ::Time.zone.now : ::Time.now - end - - private - def get_zone(time_zone) - return time_zone if time_zone.nil? || time_zone.is_a?(TimeZone) - # lookup timezone based on identifier (unless we've been passed a TZInfo::Timezone) - unless time_zone.respond_to?(:period_for_local) - time_zone = TimeZone[time_zone] || TZInfo::Timezone.get(time_zone) rescue nil - end - # Return if a TimeZone instance, or wrap in a TimeZone instance if a TZInfo::Timezone - if time_zone - time_zone.is_a?(TimeZone) ? time_zone : TimeZone.create(time_zone.name, nil, time_zone) - end - end + # Sets <tt>Time.zone</tt> to a TimeZone object for the current request/thread. + # + # This method accepts any of the following: + # + # * A Rails TimeZone object. + # * An identifier for a Rails TimeZone object (e.g., "Eastern Time (US & Canada)", <tt>-5.hours</tt>). + # * A TZInfo::Timezone object. + # * An identifier for a TZInfo::Timezone object (e.g., "America/New_York"). + # + # Here's an example of how you might set <tt>Time.zone</tt> on a per request basis -- <tt>current_user.time_zone</tt> + # just needs to return a string identifying the user's preferred TimeZone: + # + # class ApplicationController < ActionController::Base + # before_filter :set_time_zone + # + # def set_time_zone + # Time.zone = current_user.time_zone + # end + # end + def zone=(time_zone) + Thread.current[:time_zone] = get_zone(time_zone) + end + + # Allows override of <tt>Time.zone</tt> locally inside supplied block; resets <tt>Time.zone</tt> to existing value when done. + def use_zone(time_zone) + old_zone, ::Time.zone = ::Time.zone, get_zone(time_zone) + yield + ensure + ::Time.zone = old_zone + end + + # Returns <tt>Time.zone.now</tt> when <tt>config.time_zone</tt> is set, otherwise just returns <tt>Time.now</tt>. + def current + ::Time.zone_default ? ::Time.zone.now : ::Time.now + end + + private + def get_zone(time_zone) + return time_zone if time_zone.nil? || time_zone.is_a?(ActiveSupport::TimeZone) + # lookup timezone based on identifier (unless we've been passed a TZInfo::Timezone) + unless time_zone.respond_to?(:period_for_local) + time_zone = ActiveSupport::TimeZone[time_zone] || TZInfo::Timezone.get(time_zone) rescue nil end - - # Returns the simultaneous time in <tt>Time.zone</tt>. - # - # Time.zone = 'Hawaii' # => 'Hawaii' - # Time.utc(2000).in_time_zone # => Fri, 31 Dec 1999 14:00:00 HST -10:00 - # - # This method is similar to Time#localtime, except that it uses <tt>Time.zone</tt> as the local zone - # instead of the operating system's time zone. - # - # You can also pass in a TimeZone instance or string that identifies a TimeZone as an argument, - # and the conversion will be based on that zone instead of <tt>Time.zone</tt>. - # - # Time.utc(2000).in_time_zone('Alaska') # => Fri, 31 Dec 1999 15:00:00 AKST -09:00 - def in_time_zone(zone = ::Time.zone) - ActiveSupport::TimeWithZone.new(utc? ? self : getutc, ::Time.__send__(:get_zone, zone)) + # Return if a TimeZone instance, or wrap in a TimeZone instance if a TZInfo::Timezone + if time_zone + time_zone.is_a?(ActiveSupport::TimeZone) ? time_zone : ActiveSupport::TimeZone.create(time_zone.name, nil, time_zone) end end - end end -end
\ No newline at end of file + + # Returns the simultaneous time in <tt>Time.zone</tt>. + # + # Time.zone = 'Hawaii' # => 'Hawaii' + # Time.utc(2000).in_time_zone # => Fri, 31 Dec 1999 14:00:00 HST -10:00 + # + # This method is similar to Time#localtime, except that it uses <tt>Time.zone</tt> as the local zone + # instead of the operating system's time zone. + # + # You can also pass in a TimeZone instance or string that identifies a TimeZone as an argument, + # and the conversion will be based on that zone instead of <tt>Time.zone</tt>. + # + # Time.utc(2000).in_time_zone('Alaska') # => Fri, 31 Dec 1999 15:00:00 AKST -09:00 + def in_time_zone(zone = ::Time.zone) + ActiveSupport::TimeWithZone.new(utc? ? self : getutc, ::Time.__send__(:get_zone, zone)) + end +end diff --git a/activesupport/lib/active_support/core_ext/util.rb b/activesupport/lib/active_support/core_ext/util.rb new file mode 100644 index 0000000000..fc4666638c --- /dev/null +++ b/activesupport/lib/active_support/core_ext/util.rb @@ -0,0 +1,10 @@ +module ActiveSupport + class << self + def core_ext(subject, names) + names.each do |name| + require "active_support/core_ext/#{Inflector.underscore(subject.name)}/#{name}" + subject.send :include, Inflector.constantize("ActiveSupport::CoreExtensions::#{subject.name}::#{Inflector.camelize(name)}") + end + end + end +end diff --git a/activesupport/lib/active_support/dependencies.rb b/activesupport/lib/active_support/dependencies.rb index 2badad5f5f..41b7e00c0c 100644 --- a/activesupport/lib/active_support/dependencies.rb +++ b/activesupport/lib/active_support/dependencies.rb @@ -1,3 +1,11 @@ +require 'set' +require 'active_support/inflector' +require 'active_support/core_ext/name_error' +require 'active_support/core_ext/object/blank' +require 'active_support/core_ext/module/aliasing' +require 'active_support/core_ext/module/attribute_accessors' +require 'active_support/core_ext/module/introspection' + module ActiveSupport #:nodoc: module Dependencies #:nodoc: extend self @@ -16,7 +24,7 @@ module ActiveSupport #:nodoc: # Should we load files or require them? mattr_accessor :mechanism - self.mechanism = :load + self.mechanism = ENV['NO_RELOAD'] ? :require : :load # The set of directories from which we may automatically load files. Files # under these directories will be reloaded on each request in development mode, @@ -328,7 +336,7 @@ module ActiveSupport #:nodoc: # Search for a file in load_paths matching the provided suffix. def search_for_file(path_suffix) - path_suffix = path_suffix + '.rb' unless path_suffix.ends_with? '.rb' + path_suffix = "#{path_suffix}.rb" unless path_suffix =~ /\.rb\Z/ load_paths.each do |root| path = File.join(root, path_suffix) return path if File.file? path @@ -410,7 +418,7 @@ module ActiveSupport #:nodoc: # If we have an anonymous module, all we can do is attempt to load from Object. from_mod = Object if from_mod.name.blank? - unless qualified_const_defined?(from_mod.name) && from_mod.name.constantize.object_id == from_mod.object_id + unless qualified_const_defined?(from_mod.name) && Inflector.constantize(from_mod.name).object_id == from_mod.object_id raise ArgumentError, "A copy of #{from_mod} has been removed from the module tree but is still active!" end @@ -501,7 +509,7 @@ module ActiveSupport #:nodoc: # Handle the case where the module has yet to be defined. initial_constants = if qualified_const_defined?(mod_name) - mod_name.constantize.local_constant_names + Inflector.constantize(mod_name).local_constant_names else [] end @@ -526,7 +534,7 @@ module ActiveSupport #:nodoc: # Module still doesn't exist? Treat it as if it has no constants. next [] unless qualified_const_defined?(mod_name) - mod = mod_name.constantize + mod = Inflector.constantize(mod_name) next [] unless mod.is_a? Module new_constants = mod.local_constant_names - prior_constants @@ -596,7 +604,7 @@ module ActiveSupport #:nodoc: if names.size == 1 # It's under Object parent = Object else - parent = (names[0..-2] * '::').constantize + parent = Inflector.constantize(names[0..-2] * '::') end log "removing constant #{const}" diff --git a/activesupport/lib/active_support/deprecation.rb b/activesupport/lib/active_support/deprecation.rb index 202b46ce7a..e1b8211d68 100644 --- a/activesupport/lib/active_support/deprecation.rb +++ b/activesupport/lib/active_support/deprecation.rb @@ -1,196 +1,18 @@ -require 'yaml' +require 'active_support/deprecation/behaviors' +require 'active_support/deprecation/reporting' +require 'active_support/deprecation/method_wrappers' +require 'active_support/deprecation/proxy_wrappers' module ActiveSupport module Deprecation #:nodoc: - mattr_accessor :debug - self.debug = false - - # Choose the default warn behavior according to RAILS_ENV. - # Ignore deprecation warnings in production. - DEFAULT_BEHAVIORS = { - 'test' => Proc.new { |message, callstack| - $stderr.puts(message) - $stderr.puts callstack.join("\n ") if debug - }, - 'development' => Proc.new { |message, callstack| - logger = defined?(Rails) ? Rails.logger : Logger.new($stderr) - logger.warn message - logger.debug callstack.join("\n ") if debug - } - } - class << self - def warn(message = nil, callstack = caller) - behavior.call(deprecation_message(callstack, message), callstack) if behavior && !silenced? - end - - def default_behavior - if defined?(RAILS_ENV) - DEFAULT_BEHAVIORS[RAILS_ENV.to_s] - else - DEFAULT_BEHAVIORS['test'] - end - end - - # Have deprecations been silenced? - def silenced? - @silenced = false unless defined?(@silenced) - @silenced - end - - # Silence deprecation warnings within the block. - def silence - old_silenced, @silenced = @silenced, true - yield - ensure - @silenced = old_silenced - end - - attr_writer :silenced - - - private - def deprecation_message(callstack, message = nil) - message ||= "You are using deprecated behavior which will be removed from the next major or minor release." - "DEPRECATION WARNING: #{message}. #{deprecation_caller_message(callstack)}" - end - - def deprecation_caller_message(callstack) - file, line, method = extract_callstack(callstack) - if file - if line && method - "(called from #{method} at #{file}:#{line})" - else - "(called from #{file}:#{line})" - end - end - end - - def extract_callstack(callstack) - if md = callstack.first.match(/^(.+?):(\d+)(?::in `(.*?)')?/) - md.captures - else - callstack.first - end - end + # The version the deprecated behavior will be removed, by default. + attr_accessor :deprecation_horizon end + self.deprecation_horizon = '3.0' - # Behavior is a block that takes a message argument. - mattr_accessor :behavior - self.behavior = default_behavior - - # Warnings are not silenced by default. + # By default, warnings are not silenced and debugging is off. self.silenced = false - - module ClassMethods #:nodoc: - # Declare that a method has been deprecated. - def deprecate(*method_names) - options = method_names.extract_options! - method_names = method_names + options.keys - method_names.each do |method_name| - alias_method_chain(method_name, :deprecation) do |target, punctuation| - class_eval(<<-EOS, __FILE__, __LINE__ + 1) - def #{target}_with_deprecation#{punctuation}(*args, &block) # def generate_secret_with_deprecation(*args, &block) - ::ActiveSupport::Deprecation.warn( # ::ActiveSupport::Deprecation.warn( - self.class.deprecated_method_warning( # self.class.deprecated_method_warning( - :#{method_name}, # :generate_secret, - #{options[method_name].inspect}), # "You should use ActiveSupport::SecureRandom.hex(64)"), - caller # caller - ) # ) - #{target}_without_deprecation#{punctuation}(*args, &block) # generate_secret_without_deprecation(*args, &block) - end # end - EOS - end - end - end - - def deprecated_method_warning(method_name, message=nil) - warning = "#{method_name} is deprecated and will be removed from Rails #{deprecation_horizon}" - case message - when Symbol then "#{warning} (use #{message} instead)" - when String then "#{warning} (#{message})" - else warning - end - end - - def deprecation_horizon - '2.3' - end - end - - class DeprecationProxy #:nodoc: - silence_warnings do - instance_methods.each { |m| undef_method m unless m =~ /^__/ } - end - - # Don't give a deprecation warning on inspect since test/unit and error - # logs rely on it for diagnostics. - def inspect - target.inspect - end - - private - def method_missing(called, *args, &block) - warn caller, called, args - target.__send__(called, *args, &block) - 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: - def initialize(instance, method, var = "@#{method}") - @instance, @method, @var = instance, method, var - end - - private - def target - @instance.__send__(@method) - end - - def warn(callstack, called, args) - ActiveSupport::Deprecation.warn("#{@var} is deprecated! Call #{@method}.#{called} instead of #{@var}.#{called}. Args: #{args.inspect}", callstack) - end - end - - class DeprecatedConstantProxy < DeprecationProxy #:nodoc: - def initialize(old_const, new_const) - @old_const = old_const - @new_const = new_const - end - - def class - target.class - end - - private - def target - @new_const.to_s.constantize - end - - def warn(callstack, called, args) - ActiveSupport::Deprecation.warn("#{@old_const} is deprecated! Use #{@new_const} instead.", callstack) - end - end + self.debug = false end end - -class Module - include ActiveSupport::Deprecation::ClassMethods -end diff --git a/activesupport/lib/active_support/deprecation/behaviors.rb b/activesupport/lib/active_support/deprecation/behaviors.rb new file mode 100644 index 0000000000..c531a1aa58 --- /dev/null +++ b/activesupport/lib/active_support/deprecation/behaviors.rb @@ -0,0 +1,32 @@ +module ActiveSupport + module Deprecation + class << self + # Behavior is a block that takes a message argument. + attr_writer :behavior + + # Whether to print a backtrace along with the warning. + attr_accessor :debug + + def behavior + @behavior ||= default_behavior + end + + def default_behavior + Deprecation::DEFAULT_BEHAVIORS[defined?(Rails) ? Rails.env.to_s : 'test'] + end + end + + # Default warning behaviors per Rails.env. Ignored in production. + DEFAULT_BEHAVIORS = { + 'test' => Proc.new { |message, callstack| + $stderr.puts(message) + $stderr.puts callstack.join("\n ") if debug + }, + 'development' => Proc.new { |message, callstack| + logger = defined?(Rails) ? Rails.logger : Logger.new($stderr) + logger.warn message + logger.debug callstack.join("\n ") if debug + } + } + end +end diff --git a/activesupport/lib/active_support/deprecation/method_wrappers.rb b/activesupport/lib/active_support/deprecation/method_wrappers.rb new file mode 100644 index 0000000000..b35d4daf9a --- /dev/null +++ b/activesupport/lib/active_support/deprecation/method_wrappers.rb @@ -0,0 +1,27 @@ +require 'active_support/core_ext/module/deprecation' + +module ActiveSupport + class << Deprecation + # Declare that a method has been deprecated. + def deprecate_methods(target_module, *method_names) + options = method_names.extract_options! + method_names += options.keys + + method_names.each do |method_name| + target_module.alias_method_chain(method_name, :deprecation) do |target, punctuation| + target_module.module_eval(<<-end_eval, __FILE__, __LINE__ + 1) + def #{target}_with_deprecation#{punctuation}(*args, &block) # def generate_secret_with_deprecation(*args, &block) + ::ActiveSupport::Deprecation.warn( # ::ActiveSupport::Deprecation.warn( + ::ActiveSupport::Deprecation.deprecated_method_warning( # ::ActiveSupport::Deprecation.deprecated_method_warning( + :#{method_name}, # :generate_secret, + #{options[method_name].inspect}), # "You should use ActiveSupport::SecureRandom.hex(64)"), + caller # caller + ) # ) + #{target}_without_deprecation#{punctuation}(*args, &block) # generate_secret_without_deprecation(*args, &block) + end # end + end_eval + end + end + end + end +end diff --git a/activesupport/lib/active_support/deprecation/proxy_wrappers.rb b/activesupport/lib/active_support/deprecation/proxy_wrappers.rb new file mode 100644 index 0000000000..1c268d0d9c --- /dev/null +++ b/activesupport/lib/active_support/deprecation/proxy_wrappers.rb @@ -0,0 +1,72 @@ +module ActiveSupport + module Deprecation + class DeprecationProxy #:nodoc: + instance_methods.each { |m| undef_method m unless m =~ /^__/ } + + # Don't give a deprecation warning on inspect since test/unit and error + # logs rely on it for diagnostics. + def inspect + target.inspect + end + + private + def method_missing(called, *args, &block) + warn caller, called, args + target.__send__(called, *args, &block) + end + end + + class DeprecatedObjectProxy < DeprecationProxy #:nodoc: + 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: + def initialize(instance, method, var = "@#{method}") + @instance, @method, @var = instance, method, var + end + + private + def target + @instance.__send__(@method) + end + + def warn(callstack, called, args) + ActiveSupport::Deprecation.warn("#{@var} is deprecated! Call #{@method}.#{called} instead of #{@var}.#{called}. Args: #{args.inspect}", callstack) + end + end + + class DeprecatedConstantProxy < DeprecationProxy #:nodoc: + def initialize(old_const, new_const) + @old_const = old_const + @new_const = new_const + end + + def class + target.class + end + + private + def target + @new_const.to_s.constantize + end + + def warn(callstack, called, args) + ActiveSupport::Deprecation.warn("#{@old_const} is deprecated! Use #{@new_const} instead.", callstack) + end + end + end +end diff --git a/activesupport/lib/active_support/deprecation/reporting.rb b/activesupport/lib/active_support/deprecation/reporting.rb new file mode 100644 index 0000000000..fcb05ad8d9 --- /dev/null +++ b/activesupport/lib/active_support/deprecation/reporting.rb @@ -0,0 +1,55 @@ +module ActiveSupport + module Deprecation + class << self + attr_accessor :silenced + + def warn(message = nil, callstack = caller) + if behavior && !silenced + behavior.call(deprecation_message(callstack, message), callstack) + end + end + + # Silence deprecation warnings within the block. + def silence + old_silenced, @silenced = @silenced, true + yield + ensure + @silenced = old_silenced + end + + def deprecated_method_warning(method_name, message = nil) + warning = "#{method_name} is deprecated and will be removed from Rails #{deprecation_horizon}" + case message + when Symbol then "#{warning} (use #{message} instead)" + when String then "#{warning} (#{message})" + else warning + end + end + + private + def deprecation_message(callstack, message = nil) + message ||= "You are using deprecated behavior which will be removed from the next major or minor release." + "DEPRECATION WARNING: #{message}. #{deprecation_caller_message(callstack)}" + end + + def deprecation_caller_message(callstack) + file, line, method = extract_callstack(callstack) + if file + if line && method + "(called from #{method} at #{file}:#{line})" + else + "(called from #{file}:#{line})" + end + end + end + + def extract_callstack(callstack) + if md = callstack.first.match(/^(.+?):(\d+)(?::in `(.*?)')?/) + md.captures + else + callstack.first + end + end + end + end +end diff --git a/activesupport/lib/active_support/hash_with_indifferent_access.rb b/activesupport/lib/active_support/hash_with_indifferent_access.rb new file mode 100644 index 0000000000..61fc6475a0 --- /dev/null +++ b/activesupport/lib/active_support/hash_with_indifferent_access.rb @@ -0,0 +1,133 @@ +# This class has dubious semantics and we only have it so that +# people can write params[:key] instead of params['key'] +# and they get the same value for both keys. + +module ActiveSupport + class HashWithIndifferentAccess < Hash + def initialize(constructor = {}) + if constructor.is_a?(Hash) + super() + update(constructor) + else + super(constructor) + end + end + + def default(key = nil) + if key.is_a?(Symbol) && include?(key = key.to_s) + self[key] + else + super + end + end + + alias_method :regular_writer, :[]= unless method_defined?(:regular_writer) + alias_method :regular_update, :update unless method_defined?(:regular_update) + + # Assigns a new value to the hash: + # + # hash = HashWithIndifferentAccess.new + # hash[:key] = "value" + # + def []=(key, value) + regular_writer(convert_key(key), convert_value(value)) + end + + # Updates the instantized hash with values from the second: + # + # hash_1 = HashWithIndifferentAccess.new + # hash_1[:key] = "value" + # + # hash_2 = HashWithIndifferentAccess.new + # hash_2[:key] = "New Value!" + # + # hash_1.update(hash_2) # => {"key"=>"New Value!"} + # + def update(other_hash) + other_hash.each_pair { |key, value| regular_writer(convert_key(key), convert_value(value)) } + self + end + + alias_method :merge!, :update + + # Checks the hash for a key matching the argument passed in: + # + # hash = HashWithIndifferentAccess.new + # hash["key"] = "value" + # hash.key? :key # => true + # hash.key? "key" # => true + # + def key?(key) + super(convert_key(key)) + end + + alias_method :include?, :key? + alias_method :has_key?, :key? + alias_method :member?, :key? + + # Fetches the value for the specified key, same as doing hash[key] + def fetch(key, *extras) + super(convert_key(key), *extras) + end + + # Returns an array of the values at the specified indices: + # + # hash = HashWithIndifferentAccess.new + # hash[:a] = "x" + # hash[:b] = "y" + # hash.values_at("a", "b") # => ["x", "y"] + # + def values_at(*indices) + indices.collect {|key| self[convert_key(key)]} + end + + # Returns an exact copy of the hash. + def dup + HashWithIndifferentAccess.new(self) + end + + # Merges the instantized and the specified hashes together, giving precedence to the values from the second hash + # Does not overwrite the existing hash. + def merge(hash) + self.dup.update(hash) + end + + # Performs the opposite of merge, with the keys and values from the first hash taking precedence over the second. + # This overloaded definition prevents returning a regular hash, if reverse_merge is called on a HashWithDifferentAccess. + def reverse_merge(other_hash) + super other_hash.with_indifferent_access + end + + # Removes a specified key from the hash. + def delete(key) + super(convert_key(key)) + end + + def stringify_keys!; self end + def symbolize_keys!; self end + def to_options!; self end + + # Convert to a Hash with String keys. + def to_hash + Hash.new(default).merge(self) + end + + protected + def convert_key(key) + key.kind_of?(Symbol) ? key.to_s : key + end + + def convert_value(value) + case value + when Hash + value.with_indifferent_access + when Array + value.collect { |e| e.is_a?(Hash) ? e.with_indifferent_access : e } + else + value + end + end + end +end + +HashWithIndifferentAccess = ActiveSupport::HashWithIndifferentAccess diff --git a/activesupport/lib/active_support/inflector.rb b/activesupport/lib/active_support/inflector.rb index 3ed30bdf56..f14f6a6490 100644 --- a/activesupport/lib/active_support/inflector.rb +++ b/activesupport/lib/active_support/inflector.rb @@ -1,7 +1,8 @@ # encoding: utf-8 -require 'singleton' require 'iconv' +require 'active_support/core_ext/string/multibyte' + module ActiveSupport # The Inflector transforms words from singular to plural, class names to table names, modularized class names to ones without, # and class names to foreign keys. The default inflections for pluralization, singularization, and uncountable words are kept @@ -30,7 +31,9 @@ module ActiveSupport # pluralization and singularization rules that is runs. This guarantees that your rules run before any of the rules that may # already have been loaded. class Inflections - include Singleton + def self.instance + @__instance__ ||= new + end attr_reader :plurals, :singulars, :uncountables, :humans diff --git a/activesupport/lib/active_support/json/decoding.rb b/activesupport/lib/active_support/json/decoding.rb index 0e079341ff..c66500aa9c 100644 --- a/activesupport/lib/active_support/json/decoding.rb +++ b/activesupport/lib/active_support/json/decoding.rb @@ -1,5 +1,7 @@ -require 'yaml' -require 'strscan' +autoload :YAML, 'yaml' unless defined? YAML +autoload :StringScanner, 'strscan' unless defined? StringScanner + +require 'active_support/core_ext/string/starts_ends_with' module ActiveSupport module JSON @@ -45,7 +47,7 @@ module ActiveSupport if marks.empty? json.gsub(/\\([\\\/]|u[[:xdigit:]]{4})/) do ustr = $1 - if ustr.starts_with?('u') + if ustr.start_with?('u') [ustr[1..-1].to_i(16)].pack("U") elsif ustr == '\\' '\\\\' @@ -61,10 +63,10 @@ module ActiveSupport scanner.pos = left.succ output << scanner.peek(right_pos[i] - scanner.pos + 1).gsub(/\\([\\\/]|u[[:xdigit:]]{4})/) do ustr = $1 - if ustr.starts_with?('u') + if ustr.start_with?('u') [ustr[1..-1].to_i(16)].pack("U") elsif ustr == '\\' - '\\\\' + '\\\\' else ustr end diff --git a/activesupport/lib/active_support/json/encoders/hash.rb b/activesupport/lib/active_support/json/encoders/hash.rb index e38b4f3e16..d87b880743 100644 --- a/activesupport/lib/active_support/json/encoders/hash.rb +++ b/activesupport/lib/active_support/json/encoders/hash.rb @@ -1,3 +1,5 @@ +require 'active_support/core_ext/array/wrap' + class Hash # Returns a JSON string representing the hash. # diff --git a/activesupport/lib/active_support/json/encoders/object.rb b/activesupport/lib/active_support/json/encoders/object.rb index ca215d4964..0475967aee 100644 --- a/activesupport/lib/active_support/json/encoders/object.rb +++ b/activesupport/lib/active_support/json/encoders/object.rb @@ -1,3 +1,5 @@ +require 'active_support/core_ext/object/instance_variables' + class Object # Dumps object in JSON (JavaScript Object Notation). See www.json.org for more info. def to_json(options = {}) diff --git a/activesupport/lib/active_support/option_merger.rb b/activesupport/lib/active_support/option_merger.rb index 63662b75c7..e55ffd12c3 100644 --- a/activesupport/lib/active_support/option_merger.rb +++ b/activesupport/lib/active_support/option_merger.rb @@ -1,3 +1,5 @@ +require 'active_support/core_ext/hash/deep_merge' + module ActiveSupport class OptionMerger #:nodoc: instance_methods.each do |method| diff --git a/activesupport/lib/active_support/ordered_options.rb b/activesupport/lib/active_support/ordered_options.rb index 642045186f..596a7b757d 100644 --- a/activesupport/lib/active_support/ordered_options.rb +++ b/activesupport/lib/active_support/ordered_options.rb @@ -1,5 +1,7 @@ +require 'active_support/ordered_hash' + module ActiveSupport #:nodoc: - class OrderedOptions < OrderedHash #:nodoc: + class OrderedOptions < OrderedHash def []=(key, value) super(key.to_sym, value) end diff --git a/activesupport/lib/active_support/rescuable.rb b/activesupport/lib/active_support/rescuable.rb index c27c4ddb1a..a7258c870a 100644 --- a/activesupport/lib/active_support/rescuable.rb +++ b/activesupport/lib/active_support/rescuable.rb @@ -1,3 +1,6 @@ +require 'active_support/core_ext/class/inheritable_attributes' +require 'active_support/core_ext/proc' + module ActiveSupport # Rescuable module adds support for easier exception handling. module Rescuable diff --git a/activesupport/lib/active_support/time_with_zone.rb b/activesupport/lib/active_support/time_with_zone.rb index 9068afef2e..69abb80721 100644 --- a/activesupport/lib/active_support/time_with_zone.rb +++ b/activesupport/lib/active_support/time_with_zone.rb @@ -1,4 +1,9 @@ -require 'tzinfo' +require 'active_support/duration' +require 'active_support/core_ext/numeric/time' +require 'active_support/core_ext/integer/time' +require 'active_support/core_ext/time/conversions' +require 'active_support/core_ext/date/conversions' +require 'active_support/core_ext/date_time/conversions' module ActiveSupport # A Time-like class that can represent a time in any time zone. Necessary because standard Ruby Time instances are @@ -92,7 +97,7 @@ module ActiveSupport 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) + utc? && alternate_utc_string || TimeZone.seconds_to_utc_offset(utc_offset, colon) end # Time uses +zone+ to display the time zone abbreviation, so we're duck-typing it. @@ -153,8 +158,9 @@ module ActiveSupport # <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) - return utc.to_s(format) if format == :db - if formatter = ::Time::DATE_FORMATS[format] + if format == :db + utc.to_s(format) + elsif formatter = ::Time::DATE_FORMATS[format] formatter.respond_to?(:call) ? formatter.call(self).to_s : strftime(formatter) else "#{time.strftime("%Y-%m-%d %H:%M:%S")} #{formatted_offset(false, 'UTC')}" # mimicking Ruby 1.9 Time#to_s format diff --git a/activesupport/lib/active_support/values/time_zone.rb b/activesupport/lib/active_support/values/time_zone.rb index 836f469df7..e2d759aa50 100644 --- a/activesupport/lib/active_support/values/time_zone.rb +++ b/activesupport/lib/active_support/values/time_zone.rb @@ -1,3 +1,7 @@ +require 'active_support/core_ext/time' +require 'active_support/core_ext/date' +require 'active_support/core_ext/date_time' + # The TimeZone class serves as a wrapper around TZInfo::Timezone instances. It allows us to do the following: # # * Limit the set of zones provided by TZInfo to a meaningful subset of 142 zones. @@ -170,6 +174,20 @@ module ActiveSupport MAPPING.freeze end + UTC_OFFSET_WITH_COLON = '%+03d:%02d' + UTC_OFFSET_WITHOUT_COLON = UTC_OFFSET_WITH_COLON.sub(':', '') + + # Assumes self represents an offset from UTC in seconds (as returned from Time#utc_offset) + # and turns this into an +HH:MM formatted string. Example: + # + # TimeZone.seconds_to_utc_offset(-21_600) # => "-06:00" + def self.seconds_to_utc_offset(seconds, colon = true) + format = colon ? UTC_OFFSET_WITH_COLON : UTC_OFFSET_WITHOUT_COLON + hours = seconds / 3600 + minutes = (seconds.abs % 3600) / 60 + format % [hours, minutes] + end + include Comparable attr_reader :name @@ -190,7 +208,7 @@ module ActiveSupport # Returns the offset of this time zone as a formatted string, of the # format "+HH:MM". def formatted_offset(colon=true, alternate_utc_string = nil) - utc_offset == 0 && alternate_utc_string || utc_offset.to_utc_offset_s(colon) + utc_offset == 0 && alternate_utc_string || self.class.seconds_to_utc_offset(utc_offset, colon) end # Compare this time zone to the parameter. The two are comapred first on @@ -345,14 +363,13 @@ module ActiveSupport "Wellington" ], [ 46_800, "Nuku'alofa" ]]. each do |offset, *places| - places.each do |place| + places.sort.each do |place| place.freeze zone = new(place, offset) ZONES << zone ZONES_MAP[place] = zone end end - ZONES.sort! ZONES.freeze ZONES_MAP.freeze diff --git a/activesupport/lib/active_support/vendor.rb b/activesupport/lib/active_support/vendor.rb index 28852e65c8..2be9b85c89 100644 --- a/activesupport/lib/active_support/vendor.rb +++ b/activesupport/lib/active_support/vendor.rb @@ -6,7 +6,6 @@ begin rescue Gem::LoadError $:.unshift "#{File.dirname(__FILE__)}/vendor/builder-2.1.2" end -require 'builder' begin gem 'memcache-client', '>= 1.6.5' diff --git a/activesupport/lib/active_support/vendor/i18n-0.1.3/lib/i18n.rb b/activesupport/lib/active_support/vendor/i18n-0.1.3/lib/i18n.rb index 76361bed90..1b49debc05 100755 --- a/activesupport/lib/active_support/vendor/i18n-0.1.3/lib/i18n.rb +++ b/activesupport/lib/active_support/vendor/i18n-0.1.3/lib/i18n.rb @@ -1,3 +1,4 @@ +#-- # Authors:: Matt Aimonetti (http://railsontherun.com/), # Sven Fuchs (http://www.artweb-design.de), # Joshua Harvey (http://www.workingwithrails.com/person/759-joshua-harvey), @@ -5,10 +6,14 @@ # Stephan Soller (http://www.arkanis-development.de/) # Copyright:: Copyright (c) 2008 The Ruby i18n Team # License:: MIT -require 'i18n/backend/simple' -require 'i18n/exceptions' +#++ module I18n + autoload :ArgumentError, 'i18n/exceptions' + module Backend + autoload :Simple, 'i18n/backend/simple' + end + @@backend = nil @@load_path = nil @@default_locale = :'en' diff --git a/activesupport/lib/active_support/vendor/i18n-0.1.3/lib/i18n/backend/simple.rb b/activesupport/lib/active_support/vendor/i18n-0.1.3/lib/i18n/backend/simple.rb index c09acd7d2d..c32cc76f34 100644 --- a/activesupport/lib/active_support/vendor/i18n-0.1.3/lib/i18n/backend/simple.rb +++ b/activesupport/lib/active_support/vendor/i18n-0.1.3/lib/i18n/backend/simple.rb @@ -1,4 +1,4 @@ -require 'yaml' +require 'i18n/exceptions' module I18n module Backend @@ -186,6 +186,7 @@ module I18n # Loads a YAML translations file. The data must have locales as # toplevel keys. def load_yml(filename) + require 'yaml' unless defined? :YAML YAML::load(IO.read(filename)) end @@ -211,4 +212,4 @@ module I18n end end end -end
\ No newline at end of file +end diff --git a/activesupport/lib/active_support/vendor/i18n-0.1.3/lib/i18n/exceptions.rb b/activesupport/lib/active_support/vendor/i18n-0.1.3/lib/i18n/exceptions.rb index b5cea7acb4..6897055d6d 100644 --- a/activesupport/lib/active_support/vendor/i18n-0.1.3/lib/i18n/exceptions.rb +++ b/activesupport/lib/active_support/vendor/i18n-0.1.3/lib/i18n/exceptions.rb @@ -50,4 +50,4 @@ module I18n super "can not load translations from #{filename}, the file type #{type} is not known" end end -end
\ No newline at end of file +end diff --git a/activesupport/lib/active_support/xml_mini/libxml.rb b/activesupport/lib/active_support/xml_mini/libxml.rb index 3586b24a6b..370205409a 100644 --- a/activesupport/lib/active_support/xml_mini/libxml.rb +++ b/activesupport/lib/active_support/xml_mini/libxml.rb @@ -21,15 +21,15 @@ module ActiveSupport end end -module LibXML - module Conversions - module Document +module LibXML #:nodoc: + module Conversions #:nodoc: + module Document #:nodoc: def to_hash root.to_hash end end - module Node + module Node #:nodoc: CONTENT_ROOT = '__content__' LIB_XML_LIMIT = 30000000 # Hardcoded LibXML limit diff --git a/activesupport/lib/active_support/xml_mini/nokogiri.rb b/activesupport/lib/active_support/xml_mini/nokogiri.rb index 10281584fc..8f9676e4f6 100644 --- a/activesupport/lib/active_support/xml_mini/nokogiri.rb +++ b/activesupport/lib/active_support/xml_mini/nokogiri.rb @@ -18,14 +18,14 @@ module ActiveSupport end end - module Conversions - module Document + module Conversions #:nodoc: + module Document #:nodoc: def to_hash root.to_hash end end - module Node + module Node #:nodoc: CONTENT_ROOT = '__content__' # Convert XML document to hash diff --git a/activesupport/lib/active_support/xml_mini/rexml.rb b/activesupport/lib/active_support/xml_mini/rexml.rb index a8fdeca967..771109514c 100644 --- a/activesupport/lib/active_support/xml_mini/rexml.rb +++ b/activesupport/lib/active_support/xml_mini/rexml.rb @@ -56,7 +56,9 @@ module ActiveSupport hash else # must use value to prevent double-escaping - merge!(hash, CONTENT_KEY, element.texts.sum(&:value)) + texts = '' + element.texts.each { |t| texts << t.value } + merge!(hash, CONTENT_KEY, texts) end end |