diff options
Diffstat (limited to 'activesupport/lib')
25 files changed, 182 insertions, 118 deletions
diff --git a/activesupport/lib/active_support/cache.rb b/activesupport/lib/active_support/cache.rb index 1c63e8a93f..bc114e0785 100644 --- a/activesupport/lib/active_support/cache.rb +++ b/activesupport/lib/active_support/cache.rb @@ -158,20 +158,20 @@ module ActiveSupport attr_reader :silence, :options alias :silence? :silence - # Create a new cache. The options will be passed to any write method calls + # Creates a new cache. The options will be passed to any write method calls # except for <tt>:namespace</tt> which can be used to set the global # namespace for the cache. def initialize(options = nil) @options = options ? options.dup : {} end - # Silence the logger. + # Silences the logger. def silence! @silence = true self end - # Silence the logger within a block. + # Silences the logger within a block. def mute previous_silence, @silence = defined?(@silence) && @silence, true yield @@ -198,10 +198,17 @@ module ActiveSupport # cache.fetch('city') # => "Duckburgh" # # You may also specify additional options via the +options+ argument. - # Setting <tt>force: true</tt> will force a cache miss: + # Setting <tt>force: true</tt> forces a cache "miss," meaning we treat + # the cache value as missing even if it's present. Passing a block is + # required when `force` is true so this always results in a cache write. # # cache.write('today', 'Monday') - # cache.fetch('today', force: true) # => nil + # cache.fetch('today', force: true) { 'Tuesday' } # => 'Tuesday' + # cache.fetch('today', force: true) # => ArgumentError + # + # The `:force` option is useful when you're calling some other method to + # ask whether you should force a cache write. Otherwise, it's clearer to + # just call `Cache#write`. # # Setting <tt>:compress</tt> will store a large cache entry set by the call # in a compressed format. @@ -292,6 +299,8 @@ module ActiveSupport else save_block_result_to_cache(name, options) { |_name| yield _name } end + elsif options && options[:force] + raise ArgumentError, 'Missing block: Calling `Cache#fetch` with `force: true` requires a block.' else read(name, options) end @@ -323,7 +332,7 @@ module ActiveSupport end end - # Read multiple values at once from the cache. Options can be passed + # Reads multiple values at once from the cache. Options can be passed # in the last argument. # # Some cache implementation may optimize this method. @@ -413,7 +422,7 @@ module ActiveSupport end end - # Delete all entries with keys matching the pattern. + # Deletes all entries with keys matching the pattern. # # Options are passed to the underlying cache implementation. # @@ -422,7 +431,7 @@ module ActiveSupport raise NotImplementedError.new("#{self.class.name} does not support delete_matched") end - # Increment an integer value in the cache. + # Increments an integer value in the cache. # # Options are passed to the underlying cache implementation. # @@ -431,7 +440,7 @@ module ActiveSupport raise NotImplementedError.new("#{self.class.name} does not support increment") end - # Decrement an integer value in the cache. + # Decrements an integer value in the cache. # # Options are passed to the underlying cache implementation. # @@ -440,7 +449,7 @@ module ActiveSupport raise NotImplementedError.new("#{self.class.name} does not support decrement") end - # Cleanup the cache by removing expired entries. + # Cleanups the cache by removing expired entries. # # Options are passed to the underlying cache implementation. # @@ -449,7 +458,7 @@ module ActiveSupport raise NotImplementedError.new("#{self.class.name} does not support cleanup") end - # Clear the entire cache. Be careful with this method since it could + # Clears the entire cache. Be careful with this method since it could # affect other processes if shared cache is being used. # # The options hash is passed to the underlying cache implementation. @@ -460,7 +469,7 @@ module ActiveSupport end protected - # Add the namespace defined in the options to a pattern designed to + # Adds the namespace defined in the options to a pattern designed to # match keys. Implementations that support delete_matched should call # this method to translate a pattern that matches names into one that # matches namespaced keys. @@ -479,26 +488,26 @@ module ActiveSupport end end - # Read an entry from the cache implementation. Subclasses must implement + # Reads an entry from the cache implementation. Subclasses must implement # this method. def read_entry(key, options) # :nodoc: raise NotImplementedError.new end - # Write an entry to the cache implementation. Subclasses must implement + # Writes an entry to the cache implementation. Subclasses must implement # this method. def write_entry(key, entry, options) # :nodoc: raise NotImplementedError.new end - # Delete an entry from the cache implementation. Subclasses must + # Deletes an entry from the cache implementation. Subclasses must # implement this method. def delete_entry(key, options) # :nodoc: raise NotImplementedError.new end private - # Merge the default options with ones specific to a method call. + # Merges the default options with ones specific to a method call. def merged_options(call_options) # :nodoc: if call_options options.merge(call_options) @@ -507,7 +516,7 @@ module ActiveSupport end end - # Expand key to be a consistent string value. Invoke +cache_key+ if + # Expands key to be a consistent string value. Invokes +cache_key+ if # object responds to +cache_key+. Otherwise, +to_param+ method will be # called. If the key is a Hash, then keys will be sorted alphabetically. def expanded_key(key) # :nodoc: @@ -527,7 +536,7 @@ module ActiveSupport key.to_param end - # Prefix a key with the namespace. Namespace and key will be delimited + # Prefixes a key with the namespace. Namespace and key will be delimited # with a colon. def normalize_key(key, options) key = expanded_key(key) @@ -575,12 +584,12 @@ module ActiveSupport end def get_entry_value(entry, name, options) - instrument(:fetch_hit, name, options) { |payload| } + instrument(:fetch_hit, name, options) { } entry.value end def save_block_result_to_cache(name, options) - result = instrument(:generate, name, options) do |payload| + result = instrument(:generate, name, options) do yield(name) end @@ -598,7 +607,7 @@ module ActiveSupport class Entry # :nodoc: DEFAULT_COMPRESS_LIMIT = 16.kilobytes - # Create a new cache entry for the specified value. Options supported are + # Creates a new cache entry for the specified value. Options supported are # +:compress+, +:compress_threshold+, and +:expires_in+. def initialize(value, options = {}) if should_compress?(value, options) @@ -617,7 +626,7 @@ module ActiveSupport compressed? ? uncompress(@value) : @value end - # Check if the entry is expired. The +expires_in+ parameter can override + # Checks if the entry is expired. The +expires_in+ parameter can override # the value set when the entry was created. def expired? @expires_in && @created_at + @expires_in <= Time.now.to_f @@ -652,7 +661,7 @@ module ActiveSupport end end - # Duplicate the value in a class. This is used by cache implementations that don't natively + # Duplicates the value in a class. This is used by cache implementations that don't natively # serialize entries to protect against accidental cache modifications. def dup_value! if @value && !compressed? && !(@value.is_a?(Numeric) || @value == true || @value == false) diff --git a/activesupport/lib/active_support/cache/strategy/local_cache.rb b/activesupport/lib/active_support/cache/strategy/local_cache.rb index df38dbcf11..1c678dc2af 100644 --- a/activesupport/lib/active_support/cache/strategy/local_cache.rb +++ b/activesupport/lib/active_support/cache/strategy/local_cache.rb @@ -126,7 +126,7 @@ module ActiveSupport def set_cache_value(value, name, amount, options) # :nodoc: ActiveSupport::Deprecation.warn(<<-MESSAGE.strip_heredoc) `set_cache_value` is deprecated and will be removed from Rails 5.1. - Please use `write_cache_value` + Please use `write_cache_value` instead. MESSAGE write_cache_value name, value, options end diff --git a/activesupport/lib/active_support/callbacks.rb b/activesupport/lib/active_support/callbacks.rb index e6baddf5db..904d3f0eb0 100644 --- a/activesupport/lib/active_support/callbacks.rb +++ b/activesupport/lib/active_support/callbacks.rb @@ -571,15 +571,15 @@ module ActiveSupport # Install a callback for the given event. # - # set_callback :save, :before, :before_meth - # set_callback :save, :after, :after_meth, if: :condition + # set_callback :save, :before, :before_method + # set_callback :save, :after, :after_method, if: :condition # set_callback :save, :around, ->(r, block) { stuff; result = block.call; stuff } # # The second argument indicates whether the callback is to be run +:before+, # +:after+, or +:around+ the event. If omitted, +:before+ is assumed. This # means the first example above can also be written as: # - # set_callback :save, :before_meth + # set_callback :save, :before_method # # The callback can be specified as a symbol naming an instance method; as a # proc, lambda, or block; as a string to be instance evaluated(deprecated); or as an @@ -782,7 +782,7 @@ module ActiveSupport def display_deprecation_warning_for_false_terminator ActiveSupport::Deprecation.warn(<<-MSG.squish) - Returning `false` in Active Record and Active Model callbacks will not implicitly halt a callback chain in the next release of Rails. + Returning `false` in Active Record and Active Model callbacks will not implicitly halt a callback chain in Rails 5.1. To explicitly halt the callback chain, please use `throw :abort` instead. MSG end diff --git a/activesupport/lib/active_support/core_ext/date_time.rb b/activesupport/lib/active_support/core_ext/date_time.rb index bcb228b09a..5450533935 100644 --- a/activesupport/lib/active_support/core_ext/date_time.rb +++ b/activesupport/lib/active_support/core_ext/date_time.rb @@ -2,4 +2,3 @@ require 'active_support/core_ext/date_time/acts_like' require 'active_support/core_ext/date_time/blank' require 'active_support/core_ext/date_time/calculations' require 'active_support/core_ext/date_time/conversions' -require 'active_support/core_ext/date_time/zones' diff --git a/activesupport/lib/active_support/core_ext/date_time/calculations.rb b/activesupport/lib/active_support/core_ext/date_time/calculations.rb index 95617fb8c2..ac46f5ffe8 100644 --- a/activesupport/lib/active_support/core_ext/date_time/calculations.rb +++ b/activesupport/lib/active_support/core_ext/date_time/calculations.rb @@ -165,13 +165,10 @@ class DateTime # Layers additional behavior on DateTime#<=> so that Time and # ActiveSupport::TimeWithZone instances can be compared with a DateTime. def <=>(other) - if other.kind_of?(Infinity) - super - elsif other.respond_to? :to_datetime + if other.respond_to? :to_datetime super other.to_datetime rescue nil else - nil + super end 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 deleted file mode 100644 index c39f358395..0000000000 --- a/activesupport/lib/active_support/core_ext/date_time/zones.rb +++ /dev/null @@ -1,6 +0,0 @@ -require 'date' -require 'active_support/core_ext/date_and_time/zones' - -class DateTime - include DateAndTime::Zones -end diff --git a/activesupport/lib/active_support/core_ext/hash/keys.rb b/activesupport/lib/active_support/core_ext/hash/keys.rb index 8b2366c4b3..1bfa18aeee 100644 --- a/activesupport/lib/active_support/core_ext/hash/keys.rb +++ b/activesupport/lib/active_support/core_ext/hash/keys.rb @@ -11,7 +11,7 @@ class Hash # hash.transform_keys.with_index { |k, i| [k, i].join } # => {"name0"=>"Rob", "age1"=>"28"} def transform_keys return enum_for(:transform_keys) { size } unless block_given? - result = self.class.new + result = {} each_key do |key| result[yield(key)] = self[key] end diff --git a/activesupport/lib/active_support/core_ext/marshal.rb b/activesupport/lib/active_support/core_ext/marshal.rb index ca278cb2fa..edfc8296fe 100644 --- a/activesupport/lib/active_support/core_ext/marshal.rb +++ b/activesupport/lib/active_support/core_ext/marshal.rb @@ -3,7 +3,7 @@ module ActiveSupport def load(source) super(source) rescue ArgumentError, NameError => exc - if exc.message.match(%r|undefined class/module (.+)|) + if exc.message.match(%r|undefined class/module (.+?)(?:::)?\z|) # try loading the class/module loaded = $1.constantize 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 76825862d7..567ac825e9 100644 --- a/activesupport/lib/active_support/core_ext/module/attribute_accessors.rb +++ b/activesupport/lib/active_support/core_ext/module/attribute_accessors.rb @@ -27,7 +27,7 @@ class Module # <tt>instance_reader: false</tt> or <tt>instance_accessor: false</tt>. # # module HairColors - # mattr_writer :hair_colors, instance_reader: false + # mattr_reader :hair_colors, instance_reader: false # end # # class Person @@ -40,7 +40,7 @@ class Module # Also, you can pass a block to set up the attribute with a default value. # # module HairColors - # cattr_reader :hair_colors do + # mattr_reader :hair_colors do # [:brown, :black, :blonde, :red] # 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 6c4a975495..c6ece22f8d 100644 --- a/activesupport/lib/active_support/core_ext/numeric/time.rb +++ b/activesupport/lib/active_support/core_ext/numeric/time.rb @@ -25,17 +25,17 @@ class Numeric # Returns a Duration instance matching the number of minutes provided. # - # 2.minutes # => 120 seconds + # 2.minutes # => 2 minutes def minutes - ActiveSupport::Duration.new(self * 60, [[:seconds, self * 60]]) + ActiveSupport::Duration.new(self * 60, [[:minutes, self]]) end alias :minute :minutes # Returns a Duration instance matching the number of hours provided. # - # 2.hours # => 7_200 seconds + # 2.hours # => 2 hours def hours - ActiveSupport::Duration.new(self * 3600, [[:seconds, self * 3600]]) + ActiveSupport::Duration.new(self * 3600, [[:hours, self]]) end alias :hour :hours @@ -49,17 +49,17 @@ class Numeric # Returns a Duration instance matching the number of weeks provided. # - # 2.weeks # => 14 days + # 2.weeks # => 2 weeks def weeks - ActiveSupport::Duration.new(self * 7.days, [[:days, self * 7]]) + ActiveSupport::Duration.new(self * 7.days, [[:weeks, self]]) end alias :week :weeks # Returns a Duration instance matching the number of fortnights provided. # - # 2.fortnights # => 28 days + # 2.fortnights # => 4 weeks def fortnights - ActiveSupport::Duration.new(self * 2.weeks, [[:days, self * 14]]) + ActiveSupport::Duration.new(self * 2.weeks, [[:weeks, self * 2]]) end alias :fortnight :fortnights diff --git a/activesupport/lib/active_support/core_ext/object/json.rb b/activesupport/lib/active_support/core_ext/object/json.rb index 0db787010c..d49b2fbe54 100644 --- a/activesupport/lib/active_support/core_ext/object/json.rb +++ b/activesupport/lib/active_support/core_ext/object/json.rb @@ -197,3 +197,9 @@ class Process::Status #:nodoc: { :exitstatus => exitstatus, :pid => pid } end end + +class Exception + def as_json(options = nil) + to_s + end +end diff --git a/activesupport/lib/active_support/core_ext/string/conversions.rb b/activesupport/lib/active_support/core_ext/string/conversions.rb index fd79a40e31..71612e09fa 100644 --- a/activesupport/lib/active_support/core_ext/string/conversions.rb +++ b/activesupport/lib/active_support/core_ext/string/conversions.rb @@ -18,7 +18,8 @@ class String # "12/13/2012".to_time # => ArgumentError: argument out of range def to_time(form = :local) parts = Date._parse(self, false) - return if parts.empty? + used_keys = %i(year mon mday hour min sec sec_fraction offset) + return if (parts.keys & used_keys).empty? now = Time.now time = Time.new( diff --git a/activesupport/lib/active_support/core_ext/string/inflections.rb b/activesupport/lib/active_support/core_ext/string/inflections.rb index cc71b8155d..7277f51076 100644 --- a/activesupport/lib/active_support/core_ext/string/inflections.rb +++ b/activesupport/lib/active_support/core_ext/string/inflections.rb @@ -222,6 +222,15 @@ class String ActiveSupport::Inflector.humanize(self, options) end + # Converts just the first character to uppercase. + # + # 'what a Lovely Day'.upcase_first # => "What a Lovely Day" + # 'w'.upcase_first # => "W" + # ''.upcase_first # => "" + def upcase_first + ActiveSupport::Inflector.upcase_first(self) + end + # Creates a foreign key name from a class name. # +separate_class_name_and_id_with_underscore+ sets whether # the method should put '_' between the name and 'id'. diff --git a/activesupport/lib/active_support/core_ext/string/output_safety.rb b/activesupport/lib/active_support/core_ext/string/output_safety.rb index 43b9fd4bf7..005ad93b08 100644 --- a/activesupport/lib/active_support/core_ext/string/output_safety.rb +++ b/activesupport/lib/active_support/core_ext/string/output_safety.rb @@ -250,7 +250,7 @@ end class String # Marks a string as trusted safe. It will be inserted into HTML with no - # additional escaping performed. It is your responsibilty to ensure that the + # additional escaping performed. It is your responsibility to ensure that the # string contains no malicious content. This method is equivalent to the # `raw` helper in views. It is recommended that you use `sanitize` instead of # this method. It should never be called on user input. diff --git a/activesupport/lib/active_support/dependencies.rb b/activesupport/lib/active_support/dependencies.rb index 0da01b0fe8..57f6286de3 100644 --- a/activesupport/lib/active_support/dependencies.rb +++ b/activesupport/lib/active_support/dependencies.rb @@ -88,15 +88,6 @@ module ActiveSupport #:nodoc: mattr_accessor :explicitly_unloadable_constants self.explicitly_unloadable_constants = [] - # The logger is used for generating information on the action run-time - # (including benchmarking) if available. Can be set to nil for no logging. - # Compatible with both Ruby's own Logger and Log4r loggers. - mattr_accessor :logger - - # Set to +true+ to enable logging of const_missing and file loads. - mattr_accessor :log_activity - self.log_activity = false - # The WatchStack keeps a stack of the modules being watched as files are # loaded. If a file in the process of being loaded (parent.rb) triggers the # load of another file (child.rb) the stack will ensure that child.rb @@ -352,7 +343,6 @@ module ActiveSupport #:nodoc: end def clear - log_call Dependencies.unload_interlock do loaded.clear loading.clear @@ -361,7 +351,6 @@ module ActiveSupport #:nodoc: end def require_or_load(file_name, const_path = nil) - log_call file_name, const_path file_name = $` if file_name =~ /\.rb\z/ expanded = File.expand_path(file_name) return if loaded.include?(expanded) @@ -377,8 +366,6 @@ module ActiveSupport #:nodoc: begin if load? - log "loading #{file_name}" - # Enable warnings if this file has not been loaded before and # warnings_on_first_load is set. load_args = ["#{file_name}.rb"] @@ -390,7 +377,6 @@ module ActiveSupport #:nodoc: enable_warnings { result = load_file(*load_args) } end else - log "requiring #{file_name}" result = require file_name end rescue Exception @@ -483,7 +469,6 @@ module ActiveSupport #:nodoc: # set of names that the file at +path+ may define. See # +loadable_constants_for_path+ for more details. def load_file(path, const_paths = loadable_constants_for_path(path)) - log_call path, const_paths const_paths = [const_paths].compact unless const_paths.is_a? Array parent_paths = const_paths.collect { |const_path| const_path[/.*(?=::)/] || ::Object } @@ -494,7 +479,6 @@ module ActiveSupport #:nodoc: autoloaded_constants.concat newly_defined_paths unless load_once_path?(path) autoloaded_constants.uniq! - log "loading #{path} defined #{newly_defined_paths * ', '}" unless newly_defined_paths.empty? result end @@ -508,8 +492,6 @@ module ActiveSupport #:nodoc: # it is not possible to load the constant into from_mod, try its parent # module using +const_missing+. def load_missing_constant(from_mod, const_name) - log_call from_mod, const_name - unless qualified_const_defined?(from_mod.name) && Inflector.constantize(from_mod.name).equal?(from_mod) raise ArgumentError, "A copy of #{from_mod} has been removed from the module tree but is still active!" end @@ -673,25 +655,20 @@ module ActiveSupport #:nodoc: # exception, any new constants are regarded as being only partially defined # and will be removed immediately. def new_constants_in(*descs) - log_call(*descs) - constant_watch_stack.watch_namespaces(descs) - aborting = true + success = false begin yield # Now yield to the code that is to define new constants. - aborting = false + success = true ensure new_constants = constant_watch_stack.new_constants - log "New constants: #{new_constants * ', '}" - return new_constants unless aborting + return new_constants if success - log "Error during loading, removing partially loaded constants " - new_constants.each { |c| remove_constant(c) }.clear + # Remove partially loaded constants. + new_constants.each { |c| remove_constant(c) } end - - [] end # Convert the provided const desc to a qualified constant name (as a string). @@ -738,8 +715,6 @@ module ActiveSupport #:nodoc: parent = constantize(parent_name) end - log "removing constant #{const}" - # In an autoloaded user.rb like this # # autoload :Foo, 'foo' @@ -760,7 +735,7 @@ module ActiveSupport #:nodoc: begin constantized = parent.const_get(to_remove, false) rescue NameError - log "the constant #{const} is not reachable anymore, skipping" + # The constant is no longer reachable, just skip it. return else constantized.before_remove_const if constantized.respond_to?(:before_remove_const) @@ -770,27 +745,9 @@ module ActiveSupport #:nodoc: begin parent.instance_eval { remove_const to_remove } rescue NameError - log "the constant #{const} is not reachable anymore, skipping" + # The constant is no longer reachable, just skip it. end end - - protected - def log_call(*args) - if log_activity? - arg_str = args.collect(&:inspect) * ', ' - /in `([a-z_\?\!]+)'/ =~ caller(1).first - selector = $1 || '<unknown>' - log "called #{selector}(#{arg_str})" - end - end - - def log(msg) - logger.debug "Dependencies: #{msg}" if log_activity? - end - - def log_activity? - logger && log_activity - end end end diff --git a/activesupport/lib/active_support/deprecation/reporting.rb b/activesupport/lib/active_support/deprecation/reporting.rb index 35f084dd7a..de5b233679 100644 --- a/activesupport/lib/active_support/deprecation/reporting.rb +++ b/activesupport/lib/active_support/deprecation/reporting.rb @@ -65,7 +65,6 @@ module ActiveSupport def deprecation_message(callstack, message = nil) message ||= "You are using deprecated behavior which will be removed from the next major or minor release." - message += '.' unless message =~ /\.$/ "DEPRECATION WARNING: #{message} #{deprecation_caller_message(callstack)}" end diff --git a/activesupport/lib/active_support/duration.rb b/activesupport/lib/active_support/duration.rb index c63b61e97a..f67a42bcb1 100644 --- a/activesupport/lib/active_support/duration.rb +++ b/activesupport/lib/active_support/duration.rb @@ -117,7 +117,7 @@ module ActiveSupport def inspect #:nodoc: parts. reduce(::Hash.new(0)) { |h,(l,r)| h[l] += r; h }. - sort_by {|unit, _ | [:years, :months, :days, :minutes, :seconds].index(unit)}. + sort_by {|unit, _ | [:years, :months, :weeks, :days, :hours, :minutes, :seconds].index(unit)}. map {|unit, val| "#{val} #{val == 1 ? unit.to_s.chop : unit.to_s}"}. to_sentence(locale: ::I18n.default_locale) end @@ -139,6 +139,8 @@ module ActiveSupport if t.acts_like?(:time) || t.acts_like?(:date) if type == :seconds t.since(sign * number) + elsif [:hours, :minutes].include?(type) + t.in_time_zone.advance(type => sign * number) else t.advance(type => sign * number) end diff --git a/activesupport/lib/active_support/execution_wrapper.rb b/activesupport/lib/active_support/execution_wrapper.rb index 2bd1c01d35..00c5745a25 100644 --- a/activesupport/lib/active_support/execution_wrapper.rb +++ b/activesupport/lib/active_support/execution_wrapper.rb @@ -19,6 +19,32 @@ module ActiveSupport set_callback(:complete, *args, &block) end + # Register an object to be invoked during both the +run+ and + # +complete+ steps. + # + # +hook.complete+ will be passed the value returned from +hook.run+, + # and will only be invoked if +run+ has previously been called. + # (Mostly, this means it won't be invoked if an exception occurs in + # a preceding +to_run+ block; all ordinary +to_complete+ blocks are + # invoked in that situation.) + def self.register_hook(hook, outer: false) + if outer + run_args = [prepend: true] + complete_args = [:after] + else + run_args = complete_args = [] + end + + to_run(*run_args) do + hook_state[hook] = hook.run + end + to_complete(*complete_args) do + if hook_state.key?(hook) + hook.complete hook_state[hook] + end + end + end + # Run this execution. # # Returns an instance, whose +complete!+ method *must* be invoked @@ -29,7 +55,15 @@ module ActiveSupport if active? Null else - new.tap(&:run!) + new.tap do |instance| + success = nil + begin + instance.run! + success = true + ensure + instance.complete! unless success + end + end end end @@ -37,11 +71,11 @@ module ActiveSupport def self.wrap return yield if active? - state = run! + instance = run! begin yield ensure - state.complete! + instance.complete! end end @@ -74,5 +108,10 @@ module ActiveSupport ensure self.class.active.delete Thread.current end + + private + def hook_state + @_hook_state ||= {} + end end end diff --git a/activesupport/lib/active_support/file_update_checker.rb b/activesupport/lib/active_support/file_update_checker.rb index 8708a502e6..b5667b6ac8 100644 --- a/activesupport/lib/active_support/file_update_checker.rb +++ b/activesupport/lib/active_support/file_update_checker.rb @@ -1,3 +1,5 @@ +require 'active_support/core_ext/time/calculations' + module ActiveSupport # FileUpdateChecker specifies the API used by Rails to watch files # and control reloading. The API depends on four methods: @@ -112,7 +114,24 @@ module ActiveSupport # reloading is not triggered. def max_mtime(paths) time_now = Time.now - paths.map {|path| File.mtime(path)}.reject {|mtime| time_now < mtime}.max + max_mtime = nil + + # Time comparisons are performed with #compare_without_coercion because + # AS redefines these operators in a way that is much slower and does not + # bring any benefit in this particular code. + # + # Read t1.compare_without_coercion(t2) < 0 as t1 < t2. + paths.each do |path| + mtime = File.mtime(path) + + next if time_now.compare_without_coercion(mtime) < 0 + + if max_mtime.nil? || max_mtime.compare_without_coercion(mtime) < 0 + max_mtime = mtime + end + end + + max_mtime end def compile_glob(hash) diff --git a/activesupport/lib/active_support/inflector/methods.rb b/activesupport/lib/active_support/inflector/methods.rb index f741c0bfb8..f94e12e14f 100644 --- a/activesupport/lib/active_support/inflector/methods.rb +++ b/activesupport/lib/active_support/inflector/methods.rb @@ -140,6 +140,15 @@ module ActiveSupport result end + # Converts just the first character to uppercase. + # + # upcase_first('what a Lovely Day') # => "What a Lovely Day" + # upcase_first('w') # => "W" + # upcase_first('') # => "" + def upcase_first(string) + string.length > 0 ? string[0].upcase.concat(string[1..-1]) : '' + end + # Capitalizes all the words and replaces some characters in the string to # create a nicer looking title. +titleize+ is meant for creating pretty # output. It is not used in the Rails internals. diff --git a/activesupport/lib/active_support/number_helper.rb b/activesupport/lib/active_support/number_helper.rb index 55628f0313..7a49bbb960 100644 --- a/activesupport/lib/active_support/number_helper.rb +++ b/activesupport/lib/active_support/number_helper.rb @@ -15,7 +15,7 @@ module ActiveSupport extend self - # Formats a +number+ into a US phone number (e.g., (555) + # Formats a +number+ into a phone number (US by default e.g., (555) # 123-9876). You can customize the format in the +options+ hash. # # ==== Options @@ -27,6 +27,8 @@ module ActiveSupport # end of the generated number. # * <tt>:country_code</tt> - Sets the country code for the phone # number. + # * <tt>:pattern</tt> - Specifies how the number is divided into three + # groups with the custom regexp to override the default format. # ==== Examples # # number_to_phone(5551234) # => "555-1234" @@ -40,6 +42,11 @@ module ActiveSupport # # number_to_phone(1235551234, country_code: 1, extension: 1343, delimiter: '.') # # => "+1.123.555.1234 x 1343" + # + # number_to_phone(75561234567, pattern: /(\d{1,4})(\d{4})(\d{4})$/, area_code: true) + # # => "(755) 6123-4567" + # number_to_phone(13312345678, pattern: /(\d{3})(\d{4})(\d{4})$/)) + # # => "133-1234-5678" def number_to_phone(number, options = {}) NumberToPhoneConverter.convert(number, options) end diff --git a/activesupport/lib/active_support/number_helper/number_to_delimited_converter.rb b/activesupport/lib/active_support/number_helper/number_to_delimited_converter.rb index 45ae8f1a93..43c5540b6f 100644 --- a/activesupport/lib/active_support/number_helper/number_to_delimited_converter.rb +++ b/activesupport/lib/active_support/number_helper/number_to_delimited_converter.rb @@ -12,7 +12,7 @@ module ActiveSupport private def parts - left, right = number.to_s.split('.') + left, right = number.to_s.split('.'.freeze) left.gsub!(delimiter_pattern) do |digit_to_delimit| "#{digit_to_delimit}#{options[:delimiter]}" end diff --git a/activesupport/lib/active_support/number_helper/number_to_phone_converter.rb b/activesupport/lib/active_support/number_helper/number_to_phone_converter.rb index af2ee56d91..dee74fa7a6 100644 --- a/activesupport/lib/active_support/number_helper/number_to_phone_converter.rb +++ b/activesupport/lib/active_support/number_helper/number_to_phone_converter.rb @@ -18,12 +18,16 @@ module ActiveSupport end def convert_with_area_code(number) - number.gsub!(/(\d{1,3})(\d{3})(\d{4}$)/,"(\\1) \\2#{delimiter}\\3") + default_pattern = /(\d{1,3})(\d{3})(\d{4}$)/ + number.gsub!(regexp_pattern(default_pattern), + "(\\1) \\2#{delimiter}\\3") number end def convert_without_area_code(number) - number.gsub!(/(\d{0,3})(\d{3})(\d{4})$/,"\\1#{delimiter}\\2#{delimiter}\\3") + default_pattern = /(\d{0,3})(\d{3})(\d{4})$/ + number.gsub!(regexp_pattern(default_pattern), + "\\1#{delimiter}\\2#{delimiter}\\3") number.slice!(0, 1) if start_with_delimiter?(number) number end @@ -43,6 +47,11 @@ module ActiveSupport def phone_ext(ext) ext.blank? ? "" : " x #{ext}" end + + def regexp_pattern(default_pattern) + opts.fetch :pattern, default_pattern + end + end end end diff --git a/activesupport/lib/active_support/number_helper/number_to_rounded_converter.rb b/activesupport/lib/active_support/number_helper/number_to_rounded_converter.rb index 981c562551..9fb7dfb779 100644 --- a/activesupport/lib/active_support/number_helper/number_to_rounded_converter.rb +++ b/activesupport/lib/active_support/number_helper/number_to_rounded_converter.rb @@ -29,9 +29,11 @@ module ActiveSupport formatted_string = if BigDecimal === rounded_number && rounded_number.finite? - s = rounded_number.to_s('F') + '0'*precision - a, b = s.split('.', 2) - a + '.' + b[0, precision] + s = rounded_number.to_s('F') + s << '0'.freeze * precision + a, b = s.split('.'.freeze, 2) + a << '.'.freeze + a << b[0, precision] else "%00.#{precision}f" % rounded_number end diff --git a/activesupport/lib/active_support/reloader.rb b/activesupport/lib/active_support/reloader.rb index 5d1f0e1e66..5623bdd349 100644 --- a/activesupport/lib/active_support/reloader.rb +++ b/activesupport/lib/active_support/reloader.rb @@ -43,7 +43,13 @@ module ActiveSupport # Initiate a manual reload def self.reload! executor.wrap do - new.tap(&:run!).complete! + new.tap do |instance| + begin + instance.run! + ensure + instance.complete! + end + end end prepare! end |