diff options
Diffstat (limited to 'activesupport/lib/active_support/core_ext')
28 files changed, 253 insertions, 144 deletions
diff --git a/activesupport/lib/active_support/core_ext/array/inquiry.rb b/activesupport/lib/active_support/core_ext/array/inquiry.rb index de623c466c..e8f44cc378 100644 --- a/activesupport/lib/active_support/core_ext/array/inquiry.rb +++ b/activesupport/lib/active_support/core_ext/array/inquiry.rb @@ -1,3 +1,5 @@ +require 'active_support/array_inquirer' + class Array # Wraps the array in an +ArrayInquirer+ object, which gives a friendlier way # to check its string-like contents. diff --git a/activesupport/lib/active_support/core_ext/array/wrap.rb b/activesupport/lib/active_support/core_ext/array/wrap.rb index 152eb02218..b611d34c27 100644 --- a/activesupport/lib/active_support/core_ext/array/wrap.rb +++ b/activesupport/lib/active_support/core_ext/array/wrap.rb @@ -3,7 +3,7 @@ class Array # # Specifically: # - # * If the argument is +nil+ an empty list is returned. + # * If the argument is +nil+ an empty array is returned. # * Otherwise, if the argument responds to +to_ary+ it is invoked, and its result returned. # * Otherwise, returns an array with the argument as its single element. # @@ -15,12 +15,13 @@ class Array # # * If the argument responds to +to_ary+ the method is invoked. <tt>Kernel#Array</tt> # moves on to try +to_a+ if the returned value is +nil+, but <tt>Array.wrap</tt> returns - # +nil+ right away. + # an array with the argument as its single element right away. # * If the returned value from +to_ary+ is neither +nil+ nor an +Array+ object, <tt>Kernel#Array</tt> # raises an exception, while <tt>Array.wrap</tt> does not, it just returns the value. - # * It does not call +to_a+ on the argument, but returns an empty array if argument is +nil+. + # * It does not call +to_a+ on the argument, if the argument does not respond to +to_ary+ + # it returns an array with the argument as its single element. # - # The second point is easily explained with some enumerables: + # The last point is easily explained with some enumerables: # # Array(foo: :bar) # => [[:foo, :bar]] # Array.wrap(foo: :bar) # => [{:foo=>:bar}] diff --git a/activesupport/lib/active_support/core_ext/class.rb b/activesupport/lib/active_support/core_ext/class.rb index c750a10bb2..ef903d59b5 100644 --- a/activesupport/lib/active_support/core_ext/class.rb +++ b/activesupport/lib/active_support/core_ext/class.rb @@ -1,3 +1,2 @@ require 'active_support/core_ext/class/attribute' -require 'active_support/core_ext/class/delegating_attributes' require 'active_support/core_ext/class/subclasses' diff --git a/activesupport/lib/active_support/core_ext/class/delegating_attributes.rb b/activesupport/lib/active_support/core_ext/class/delegating_attributes.rb deleted file mode 100644 index 1c305c5970..0000000000 --- a/activesupport/lib/active_support/core_ext/class/delegating_attributes.rb +++ /dev/null @@ -1,45 +0,0 @@ -require 'active_support/core_ext/kernel/singleton_class' -require 'active_support/core_ext/module/remove_method' -require 'active_support/core_ext/module/deprecation' - - -class Class - def superclass_delegating_accessor(name, options = {}) - # Create private _name and _name= methods that can still be used if the public - # methods are overridden. - _superclass_delegating_accessor("_#{name}", options) - - # Generate the public methods name, name=, and name?. - # These methods dispatch to the private _name, and _name= methods, making them - # overridable. - singleton_class.send(:define_method, name) { send("_#{name}") } - singleton_class.send(:define_method, "#{name}?") { !!send("_#{name}") } - singleton_class.send(:define_method, "#{name}=") { |value| send("_#{name}=", value) } - - # If an instance_reader is needed, generate public instance methods name and name?. - if options[:instance_reader] != false - define_method(name) { send("_#{name}") } - define_method("#{name}?") { !!send("#{name}") } - end - end - - deprecate superclass_delegating_accessor: :class_attribute - - private - # Take the object being set and store it in a method. This gives us automatic - # inheritance behavior, without having to store the object in an instance - # variable and look up the superclass chain manually. - def _stash_object_in_method(object, method, instance_reader = true) - singleton_class.remove_possible_method(method) - singleton_class.send(:define_method, method) { object } - remove_possible_method(method) - define_method(method) { object } if instance_reader - end - - def _superclass_delegating_accessor(name, options = {}) - singleton_class.send(:define_method, "#{name}=") do |value| - _stash_object_in_method(value, name, options[:instance_reader] != false) - end - send("#{name}=", nil) - 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 df419a6e63..31479a1269 100644 --- a/activesupport/lib/active_support/core_ext/date/conversions.rb +++ b/activesupport/lib/active_support/core_ext/date/conversions.rb @@ -35,6 +35,7 @@ class Date # date.to_s(:db) # => "2007-11-10" # # date.to_formatted_s(:short) # => "10 Nov" + # date.to_formatted_s(:number) # => "20071110" # 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" @@ -82,6 +83,11 @@ class Date ::Time.send(form, year, month, day) end + # Returns a string which represents the time in used time zone as DateTime + # defined by XML Schema: + # + # date = Date.new(2015, 05, 23) # => Sat, 23 May 2015 + # date.xmlschema # => "2015-05-23T00:00:00+04:00" def xmlschema in_time_zone.xmlschema end diff --git a/activesupport/lib/active_support/core_ext/date_and_time/calculations.rb b/activesupport/lib/active_support/core_ext/date_and_time/calculations.rb index 9525c10112..40811dafc0 100644 --- a/activesupport/lib/active_support/core_ext/date_and_time/calculations.rb +++ b/activesupport/lib/active_support/core_ext/date_and_time/calculations.rb @@ -92,15 +92,28 @@ module DateAndTime end # Returns a new date/time at the start of the month. - # DateTime objects will have a time set to 0:00. + # + # today = Date.today # => Thu, 18 Jun 2015 + # today.beginning_of_month # => Mon, 01 Jun 2015 + # + # +DateTime+ objects will have a time set to 0:00. + # + # now = DateTime.current # => Thu, 18 Jun 2015 15:23:13 +0000 + # now.beginning_of_month # => Mon, 01 Jun 2015 00:00:00 +0000 def beginning_of_month first_hour(change(:day => 1)) end alias :at_beginning_of_month :beginning_of_month # Returns a new date/time at the start of the quarter. - # Example: 1st January, 1st July, 1st October. - # DateTime objects will have a time set to 0:00. + # + # today = Date.today # => Fri, 10 Jul 2015 + # today.beginning_of_quarter # => Wed, 01 Jul 2015 + # + # +DateTime+ objects will have a time set to 0:00. + # + # now = DateTime.current # => Fri, 10 Jul 2015 18:41:29 +0000 + # now.beginning_of_quarter # => Wed, 01 Jul 2015 00:00:00 +0000 def beginning_of_quarter first_quarter_month = [10, 7, 4, 1].detect { |m| m <= month } beginning_of_month.change(:month => first_quarter_month) @@ -108,8 +121,14 @@ module DateAndTime alias :at_beginning_of_quarter :beginning_of_quarter # Returns a new date/time at the end of the quarter. - # Example: 31st March, 30th June, 30th September. - # DateTime objects will have a time set to 23:59:59. + # + # today = Date.today # => Fri, 10 Jul 2015 + # today.end_of_quarter # => Wed, 30 Sep 2015 + # + # +DateTime+ objects will have a time set to 23:59:59. + # + # now = DateTime.current # => Fri, 10 Jul 2015 18:41:29 +0000 + # now.end_of_quarter # => Wed, 30 Sep 2015 23:59:59 +0000 def end_of_quarter last_quarter_month = [3, 6, 9, 12].detect { |m| m >= month } beginning_of_month.change(:month => last_quarter_month).end_of_month @@ -117,17 +136,35 @@ module DateAndTime alias :at_end_of_quarter :end_of_quarter # Return a new date/time at the beginning of the year. - # Example: 1st January. - # DateTime objects will have a time set to 0:00. + # + # today = Date.today # => Fri, 10 Jul 2015 + # today.beginning_of_year # => Thu, 01 Jan 2015 + # + # +DateTime+ objects will have a time set to 0:00. + # + # now = DateTime.current # => Fri, 10 Jul 2015 18:41:29 +0000 + # now.beginning_of_year # => Thu, 01 Jan 2015 00:00:00 +0000 def beginning_of_year change(:month => 1).beginning_of_month end alias :at_beginning_of_year :beginning_of_year # Returns a new date/time representing the given day in the next week. + # + # today = Date.today # => Thu, 07 May 2015 + # today.next_week # => Mon, 11 May 2015 + # # The +given_day_in_next_week+ defaults to the beginning of the week # which is determined by +Date.beginning_of_week+ or +config.beginning_of_week+ - # when set. +DateTime+ objects have their time set to 0:00 unless +same_time+ is true. + # when set. + # + # today = Date.today # => Thu, 07 May 2015 + # today.next_week(:friday) # => Fri, 15 May 2015 + # + # +DateTime+ objects have their time set to 0:00 unless +same_time+ is true. + # + # now = DateTime.current # => Thu, 07 May 2015 13:31:16 +0000 + # now.next_week # => Mon, 11 May 2015 00:00:00 +0000 def next_week(given_day_in_next_week = Date.beginning_of_week, same_time: false) result = first_hour(weeks_since(1).beginning_of_week.days_since(days_span(given_day_in_next_week))) same_time ? copy_time_to(result) : result 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 55ad384f4f..95617fb8c2 100644 --- a/activesupport/lib/active_support/core_ext/date_time/calculations.rb +++ b/activesupport/lib/active_support/core_ext/date_time/calculations.rb @@ -168,7 +168,7 @@ class DateTime if other.kind_of?(Infinity) super elsif other.respond_to? :to_datetime - super other.to_datetime + super other.to_datetime rescue nil else nil end diff --git a/activesupport/lib/active_support/core_ext/enumerable.rb b/activesupport/lib/active_support/core_ext/enumerable.rb index 7a893292b3..fc7531d088 100644 --- a/activesupport/lib/active_support/core_ext/enumerable.rb +++ b/activesupport/lib/active_support/core_ext/enumerable.rb @@ -71,6 +71,21 @@ module Enumerable def without(*elements) reject { |element| elements.include?(element) } end + + # Convert an enumerable to an array based on the given key. + # + # [{ name: "David" }, { name: "Rafael" }, { name: "Aaron" }].pluck(:name) + # => ["David", "Rafael", "Aaron"] + # + # [{ id: 1, name: "David" }, { id: 2, name: "Rafael" }].pluck(:id, :name) + # => [[1, "David"], [2, "Rafael"]] + def pluck(*keys) + if keys.many? + map { |element| keys.map { |key| element[key] } } + else + map { |element| element[keys.first] } + end + end end class Range #:nodoc: diff --git a/activesupport/lib/active_support/core_ext/hash/conversions.rb b/activesupport/lib/active_support/core_ext/hash/conversions.rb index 2149d4439d..8594d9bf2e 100644 --- a/activesupport/lib/active_support/core_ext/hash/conversions.rb +++ b/activesupport/lib/active_support/core_ext/hash/conversions.rb @@ -106,7 +106,25 @@ class Hash # # => {"hash"=>{"foo"=>1, "bar"=>2}} # # +DisallowedType+ is raised if the XML contains attributes with <tt>type="yaml"</tt> or - # <tt>type="symbol"</tt>. Use <tt>Hash.from_trusted_xml</tt> to parse this XML. + # <tt>type="symbol"</tt>. Use <tt>Hash.from_trusted_xml</tt> to + # parse this XML. + # + # Custom +disallowed_types+ can also be passed in the form of an + # array. + # + # xml = <<-XML + # <?xml version="1.0" encoding="UTF-8"?> + # <hash> + # <foo type="integer">1</foo> + # <bar type="string">"David"</bar> + # </hash> + # XML + # + # hash = Hash.from_xml(xml, ['integer']) + # # => ActiveSupport::XMLConverter::DisallowedType: Disallowed type attribute: "integer" + # + # Note that passing custom disallowed types will override the default types, + # which are Symbol and YAML. def from_xml(xml, disallowed_types = nil) ActiveSupport::XMLConverter.new(xml, disallowed_types).to_h end diff --git a/activesupport/lib/active_support/core_ext/hash/except.rb b/activesupport/lib/active_support/core_ext/hash/except.rb index 6e397abf51..2f6d38c1f6 100644 --- a/activesupport/lib/active_support/core_ext/hash/except.rb +++ b/activesupport/lib/active_support/core_ext/hash/except.rb @@ -1,8 +1,9 @@ class Hash - # Returns a hash that includes everything but the given keys. - # hash = { a: true, b: false, c: nil} - # hash.except(:c) # => { a: true, b: false} - # hash # => { a: true, b: false, c: nil} + # Returns a hash that includes everything except given keys. + # hash = { a: true, b: false, c: nil } + # hash.except(:c) # => { a: true, b: false } + # hash.except(:a, :b) # => { c: nil } + # hash # => { a: true, b: false, c: nil } # # This is useful for limiting a set of parameters to everything but a few known toggles: # @person.update(params[:person].except(:admin)) @@ -10,10 +11,10 @@ class Hash dup.except!(*keys) end - # Replaces the hash without the given keys. - # hash = { a: true, b: false, c: nil} - # hash.except!(:c) # => { a: true, b: false} - # hash # => { a: true, b: false } + # Removes the given keys from hash and returns it. + # hash = { a: true, b: false, c: nil } + # hash.except!(:c) # => { a: true, b: false } + # hash # => { a: true, b: false } def except!(*keys) keys.each { |key| delete(key) } self diff --git a/activesupport/lib/active_support/core_ext/load_error.rb b/activesupport/lib/active_support/core_ext/load_error.rb index d9fb392752..60732eb41a 100644 --- a/activesupport/lib/active_support/core_ext/load_error.rb +++ b/activesupport/lib/active_support/core_ext/load_error.rb @@ -23,7 +23,7 @@ class LoadError # Returns true if the given path name (except perhaps for the ".rb" # extension) is the missing file which caused the exception to be raised. def is_missing?(location) - location.sub(/\.rb$/, '') == path.sub(/\.rb$/, '') + location.sub(/\.rb$/, ''.freeze) == path.sub(/\.rb$/, ''.freeze) end end diff --git a/activesupport/lib/active_support/core_ext/module/aliasing.rb b/activesupport/lib/active_support/core_ext/module/aliasing.rb index a4c40b25ff..b6934b9c54 100644 --- a/activesupport/lib/active_support/core_ext/module/aliasing.rb +++ b/activesupport/lib/active_support/core_ext/module/aliasing.rb @@ -1,4 +1,7 @@ class Module + # NOTE: This method is deprecated. Please use <tt>Module#prepend</tt> that + # comes with Ruby 2.0 or newer instead. + # # Encapsulates the common pattern of: # # alias_method :foo_without_feature, :foo 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 d4e6b5a1ac..a77da573fe 100644 --- a/activesupport/lib/active_support/core_ext/module/attribute_accessors.rb +++ b/activesupport/lib/active_support/core_ext/module/attribute_accessors.rb @@ -206,7 +206,7 @@ class Module # Person.class_variable_get("@@hair_colors") # => [:brown, :black, :blonde, :red] def mattr_accessor(*syms, &blk) mattr_reader(*syms, &blk) - mattr_writer(*syms, &blk) + mattr_writer(*syms) end alias :cattr_accessor :mattr_accessor end diff --git a/activesupport/lib/active_support/core_ext/module/concerning.rb b/activesupport/lib/active_support/core_ext/module/concerning.rb index 07a392404e..65b88b9bbd 100644 --- a/activesupport/lib/active_support/core_ext/module/concerning.rb +++ b/activesupport/lib/active_support/core_ext/module/concerning.rb @@ -63,10 +63,10 @@ class Module # # == Mix-in noise exiled to its own file: # - # Once our chunk of behavior starts pushing the scroll-to-understand it's + # Once our chunk of behavior starts pushing the scroll-to-understand-it # boundary, we give in and move it to a separate file. At this size, the - # overhead feels in good proportion to the size of our extraction, despite - # diluting our at-a-glance sense of how things really work. + # increased overhead can be a reasonable tradeoff even if it reduces our + # at-a-glance perception of how things work. # # class Todo # # Other todo implementation @@ -99,7 +99,7 @@ class Module # end # # Todo.ancestors - # # => Todo, Todo::EventTracking, Object + # # => [Todo, Todo::EventTracking, Object] # # This small step has some wonderful ripple effects. We can # * grok the behavior of our class in one glance, diff --git a/activesupport/lib/active_support/core_ext/module/delegation.rb b/activesupport/lib/active_support/core_ext/module/delegation.rb index 9b7a429db9..9dc0dee1bd 100644 --- a/activesupport/lib/active_support/core_ext/module/delegation.rb +++ b/activesupport/lib/active_support/core_ext/module/delegation.rb @@ -167,7 +167,7 @@ class Module '' end - file, line = caller(1, 1).first.split(':', 2) + file, line = caller(1, 1).first.split(':'.freeze, 2) line = line.to_i to = to.to_s diff --git a/activesupport/lib/active_support/core_ext/numeric.rb b/activesupport/lib/active_support/core_ext/numeric.rb index a6bc0624be..bcdc3eace2 100644 --- a/activesupport/lib/active_support/core_ext/numeric.rb +++ b/activesupport/lib/active_support/core_ext/numeric.rb @@ -1,3 +1,4 @@ require 'active_support/core_ext/numeric/bytes' require 'active_support/core_ext/numeric/time' +require 'active_support/core_ext/numeric/inquiry' require 'active_support/core_ext/numeric/conversions' diff --git a/activesupport/lib/active_support/core_ext/numeric/inquiry.rb b/activesupport/lib/active_support/core_ext/numeric/inquiry.rb new file mode 100644 index 0000000000..7e7ac1b0b2 --- /dev/null +++ b/activesupport/lib/active_support/core_ext/numeric/inquiry.rb @@ -0,0 +1,26 @@ +unless 1.respond_to?(:positive?) # TODO: Remove this file when we drop support to ruby < 2.3 +class Numeric + # Returns true if the number is positive. + # + # 1.positive? # => true + # 0.positive? # => false + # -1.positive? # => false + def positive? + self > 0 + end + + # Returns true if the number is negative. + # + # -1.negative? # => true + # 0.negative? # => false + # 1.negative? # => false + def negative? + self < 0 + end +end + +class Complex + undef :positive? + undef :negative? +end +end diff --git a/activesupport/lib/active_support/core_ext/object/deep_dup.rb b/activesupport/lib/active_support/core_ext/object/deep_dup.rb index 0191d2e973..ad5b2af161 100644 --- a/activesupport/lib/active_support/core_ext/object/deep_dup.rb +++ b/activesupport/lib/active_support/core_ext/object/deep_dup.rb @@ -40,6 +40,7 @@ class Hash # dup[:a][:c] # => "c" def deep_dup each_with_object(dup) do |(key, value), hash| + hash.delete(key) hash[key.deep_dup] = value.deep_dup end end diff --git a/activesupport/lib/active_support/core_ext/object/duplicable.rb b/activesupport/lib/active_support/core_ext/object/duplicable.rb index 620f7b6561..befa5aee21 100644 --- a/activesupport/lib/active_support/core_ext/object/duplicable.rb +++ b/activesupport/lib/active_support/core_ext/object/duplicable.rb @@ -19,7 +19,7 @@ class Object # Can you safely dup this object? # - # False for +nil+, +false+, +true+, symbol, number objects; + # False for +nil+, +false+, +true+, symbol, number, method objects; # true otherwise. def duplicable? true @@ -78,6 +78,10 @@ end require 'bigdecimal' class BigDecimal + # BigDecimals are duplicable: + # + # BigDecimal.new("1.2").duplicable? # => true + # BigDecimal.new("1.2").dup # => #<BigDecimal:...,'0.12E1',18(18)> def duplicable? true end diff --git a/activesupport/lib/active_support/core_ext/object/try.rb b/activesupport/lib/active_support/core_ext/object/try.rb index e0f70b9caa..69be6c4abc 100644 --- a/activesupport/lib/active_support/core_ext/object/try.rb +++ b/activesupport/lib/active_support/core_ext/object/try.rb @@ -1,4 +1,34 @@ +require 'delegate' + +module ActiveSupport + module Tryable #:nodoc: + def try(*a, &b) + try!(*a, &b) if a.empty? || respond_to?(a.first) + end + + def try!(*a, &b) + if a.empty? && block_given? + if b.arity.zero? + instance_eval(&b) + else + yield self + end + else + public_send(*a, &b) + end + end + end +end + class Object + include ActiveSupport::Tryable + + ## + # :method: try + # + # :call-seq: + # try(*a, &b) + # # Invokes the public method whose name goes as first argument just like # +public_send+ does, except that if the receiver does not respond to it the # call returns +nil+ rather than raising an exception. @@ -56,30 +86,40 @@ class Object # # Please also note that +try+ is defined on +Object+. Therefore, it won't work # with instances of classes that do not have +Object+ among their ancestors, - # like direct subclasses of +BasicObject+. For example, using +try+ with - # +SimpleDelegator+ will delegate +try+ to the target instead of calling it on - # the delegator itself. - def try(*a, &b) - try!(*a, &b) if a.empty? || respond_to?(a.first) - end + # like direct subclasses of +BasicObject+. + ## + # :method: try! + # + # :call-seq: + # try!(*a, &b) + # # Same as #try, but raises a NoMethodError exception if the receiver is # not +nil+ and does not implement the tried method. # # "a".try!(:upcase) # => "A" # nil.try!(:upcase) # => nil # 123.try!(:upcase) # => NoMethodError: undefined method `upcase' for 123:Fixnum - def try!(*a, &b) - if a.empty? && block_given? - if b.arity.zero? - instance_eval(&b) - else - yield self - end - else - public_send(*a, &b) - end - end +end + +class Delegator + include ActiveSupport::Tryable + + ## + # :method: try + # + # :call-seq: + # try(a*, &b) + # + # See Object#try + + ## + # :method: try! + # + # :call-seq: + # try!(a*, &b) + # + # See Object#try! end class NilClass diff --git a/activesupport/lib/active_support/core_ext/object/with_options.rb b/activesupport/lib/active_support/core_ext/object/with_options.rb index 7d38e1d134..513c8b1d55 100644 --- a/activesupport/lib/active_support/core_ext/object/with_options.rb +++ b/activesupport/lib/active_support/core_ext/object/with_options.rb @@ -7,7 +7,7 @@ class Object # provided. Each method called on the block variable must take an options # hash as its final argument. # - # Without <tt>with_options></tt>, this code contains duplication: + # Without <tt>with_options</tt>, this code contains duplication: # # class Account < ActiveRecord::Base # has_many :customers, dependent: :destroy diff --git a/activesupport/lib/active_support/core_ext/range/each.rb b/activesupport/lib/active_support/core_ext/range/each.rb index f666480fe6..dc6dad5ced 100644 --- a/activesupport/lib/active_support/core_ext/range/each.rb +++ b/activesupport/lib/active_support/core_ext/range/each.rb @@ -1,27 +1,21 @@ -class Range #:nodoc: +module ActiveSupport + module EachTimeWithZone #:nodoc: + def each(&block) + ensure_iteration_allowed + super + end - def each_with_time_with_zone(&block) - ensure_iteration_allowed - each_without_time_with_zone(&block) - end - # TODO: change to Module#prepend as soon as the fix is backported to MRI 2.2: - # https://bugs.ruby-lang.org/issues/10847 - alias_method :each_without_time_with_zone, :each - alias_method :each, :each_with_time_with_zone + def step(n = 1, &block) + ensure_iteration_allowed + super + end - def step_with_time_with_zone(n = 1, &block) - ensure_iteration_allowed - step_without_time_with_zone(n, &block) - end - # TODO: change to Module#prepend as soon as the fix is backported to MRI 2.2: - # https://bugs.ruby-lang.org/issues/10847 - alias_method :step_without_time_with_zone, :step - alias_method :step, :step_with_time_with_zone + private - private - def ensure_iteration_allowed - if first.is_a?(Time) - raise TypeError, "can't iterate from #{first.class}" - end + def ensure_iteration_allowed + raise TypeError, "can't iterate from #{first.class}" if first.is_a?(Time) + end end end + +Range.prepend(ActiveSupport::EachTimeWithZone) 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 9d20920dd0..c69e1e3fb9 100644 --- a/activesupport/lib/active_support/core_ext/range/include_range.rb +++ b/activesupport/lib/active_support/core_ext/range/include_range.rb @@ -1,23 +1,23 @@ -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) - # 1...10 includes 1..9 but it does not include 1..10. - operator = exclude_end? && !value.exclude_end? ? :< : :<= - include_without_range?(value.first) && value.last.send(operator, last) - else - include_without_range?(value) +module ActiveSupport + module IncludeWithRange #:nodoc: + # 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?(value) + if value.is_a?(::Range) + # 1...10 includes 1..9 but it does not include 1..10. + operator = exclude_end? && !value.exclude_end? ? :< : :<= + super(value.first) && value.last.send(operator, last) + else + super + end end end - # TODO: change to Module#prepend as soon as the fix is backported to MRI 2.2: - # https://bugs.ruby-lang.org/issues/10847 - alias_method :include_without_range?, :include? - alias_method :include?, :include_with_range? end + +Range.prepend(ActiveSupport::IncludeWithRange) diff --git a/activesupport/lib/active_support/core_ext/string/filters.rb b/activesupport/lib/active_support/core_ext/string/filters.rb index 7461d03acc..375ec1aef8 100644 --- a/activesupport/lib/active_support/core_ext/string/filters.rb +++ b/activesupport/lib/active_support/core_ext/string/filters.rb @@ -17,9 +17,8 @@ class String # str.squish! # => "foo bar boo" # str # => "foo bar boo" def squish! - gsub!(/\A[[:space:]]+/, '') - gsub!(/[[:space:]]+\z/, '') gsub!(/[[:space:]]+/, ' ') + strip! self end diff --git a/activesupport/lib/active_support/core_ext/string/inflections.rb b/activesupport/lib/active_support/core_ext/string/inflections.rb index 97f9720b2b..b2e713077c 100644 --- a/activesupport/lib/active_support/core_ext/string/inflections.rb +++ b/activesupport/lib/active_support/core_ext/string/inflections.rb @@ -164,7 +164,7 @@ class String # # <%= link_to(@person.name, person_path) %> # # => <a href="/person/1-donald-e-knuth">Donald E. Knuth</a> - def parameterize(sep = '-') + def parameterize(sep = '-'.freeze) ActiveSupport::Inflector.parameterize(self, sep) end @@ -172,7 +172,7 @@ class String # uses the +pluralize+ method on the last word in the string. # # 'RawScaledScorer'.tableize # => "raw_scaled_scorers" - # 'egg_and_ham'.tableize # => "egg_and_hams" + # 'ham_and_egg'.tableize # => "ham_and_eggs" # 'fancyCategory'.tableize # => "fancy_categories" def tableize ActiveSupport::Inflector.tableize(self) @@ -182,7 +182,7 @@ class String # Note that this returns a string and not a class. (To convert to an actual class # follow +classify+ with +constantize+.) # - # 'egg_and_hams'.classify # => "EggAndHam" + # 'ham_and_eggs'.classify # => "HamAndEgg" # 'posts'.classify # => "Post" def classify ActiveSupport::Inflector.classify(self) diff --git a/activesupport/lib/active_support/core_ext/time/calculations.rb b/activesupport/lib/active_support/core_ext/time/calculations.rb index 1ce68ea7c7..96156deebb 100644 --- a/activesupport/lib/active_support/core_ext/time/calculations.rb +++ b/activesupport/lib/active_support/core_ext/time/calculations.rb @@ -98,7 +98,7 @@ class Time elsif zone ::Time.local(new_year, new_month, new_day, new_hour, new_min, new_sec, new_usec) else - raise ArgumentError, 'argument out of range' if new_usec > 999999 + raise ArgumentError, 'argument out of range' if new_usec >= 1000000 ::Time.new(new_year, new_month, new_day, new_hour, new_min, new_sec + (new_usec.to_r / 1000000), utc_offset) end end @@ -108,6 +108,12 @@ class Time # 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>. + # + # Time.new(2015, 8, 1, 14, 35, 0).advance(seconds: 1) # => 2015-08-01 14:35:01 -0700 + # Time.new(2015, 8, 1, 14, 35, 0).advance(minutes: 1) # => 2015-08-01 14:36:00 -0700 + # Time.new(2015, 8, 1, 14, 35, 0).advance(hours: 1) # => 2015-08-01 15:35:00 -0700 + # Time.new(2015, 8, 1, 14, 35, 0).advance(days: 1) # => 2015-08-02 14:35:00 -0700 + # Time.new(2015, 8, 1, 14, 35, 0).advance(weeks: 1) # => 2015-08-08 14:35:00 -0700 def advance(options) unless options[:weeks].nil? options[:weeks], partial_weeks = options[:weeks].divmod(1) diff --git a/activesupport/lib/active_support/core_ext/time/zones.rb b/activesupport/lib/active_support/core_ext/time/zones.rb index d683e7c777..133d3938eb 100644 --- a/activesupport/lib/active_support/core_ext/time/zones.rb +++ b/activesupport/lib/active_support/core_ext/time/zones.rb @@ -65,7 +65,8 @@ class Time if !time_zone || time_zone.is_a?(ActiveSupport::TimeZone) time_zone else - # lookup timezone based on identifier (unless we've been passed a TZInfo::Timezone) + # Look up the timezone based on the 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) end diff --git a/activesupport/lib/active_support/core_ext/uri.rb b/activesupport/lib/active_support/core_ext/uri.rb index bfe0832b37..0b2ff817c3 100644 --- a/activesupport/lib/active_support/core_ext/uri.rb +++ b/activesupport/lib/active_support/core_ext/uri.rb @@ -12,7 +12,7 @@ unless str == parser.unescape(parser.escape(str)) # YK: My initial experiments say yes, but let's be sure please enc = str.encoding enc = Encoding::UTF_8 if enc == Encoding::US_ASCII - str.gsub(escaped) { [$&[1, 2].hex].pack('C') }.force_encoding(enc) + str.gsub(escaped) { |match| [match[1, 2].hex].pack('C') }.force_encoding(enc) end end end |