aboutsummaryrefslogtreecommitdiffstats
path: root/activesupport/lib/active_support
diff options
context:
space:
mode:
Diffstat (limited to 'activesupport/lib/active_support')
-rw-r--r--activesupport/lib/active_support/buffered_logger.rb116
-rw-r--r--activesupport/lib/active_support/cache.rb1
-rw-r--r--activesupport/lib/active_support/cache/null_store.rb44
-rw-r--r--activesupport/lib/active_support/core_ext/string/multibyte.rb2
-rw-r--r--activesupport/lib/active_support/core_ext/string/output_safety.rb42
-rw-r--r--activesupport/lib/active_support/dependencies.rb6
-rw-r--r--activesupport/lib/active_support/file_update_checker.rb115
-rw-r--r--activesupport/lib/active_support/hash_with_indifferent_access.rb4
-rw-r--r--activesupport/lib/active_support/i18n_railtie.rb13
-rw-r--r--activesupport/lib/active_support/inflections.rb6
-rw-r--r--activesupport/lib/active_support/tagged_logging.rb4
-rw-r--r--activesupport/lib/active_support/whiny_nil.rb42
12 files changed, 236 insertions, 159 deletions
diff --git a/activesupport/lib/active_support/buffered_logger.rb b/activesupport/lib/active_support/buffered_logger.rb
index 136e245859..775f9928e6 100644
--- a/activesupport/lib/active_support/buffered_logger.rb
+++ b/activesupport/lib/active_support/buffered_logger.rb
@@ -1,5 +1,9 @@
require 'thread'
+require 'logger'
+require 'active_support/core_ext/logger'
require 'active_support/core_ext/class/attribute_accessors'
+require 'active_support/deprecation'
+require 'fileutils'
module ActiveSupport
# Inspired by the buffered logger idea by Ezra
@@ -25,40 +29,35 @@ module ActiveSupport
# Silences the logger for the duration of the block.
def silence(temporary_level = ERROR)
if silencer
- old_logger_level = @tmp_levels[Thread.current]
begin
- @tmp_levels[Thread.current] = temporary_level
- yield self
+ logger = self.class.new @log_dest, temporary_level
+ yield logger
ensure
- if old_logger_level
- @tmp_levels[Thread.current] = old_logger_level
- else
- @tmp_levels.delete(Thread.current)
- end
+ logger.close
end
else
yield self
end
end
+ deprecate :silence
- attr_writer :level
attr_reader :auto_flushing
+ deprecate :auto_flushing
def initialize(log, level = DEBUG)
@level = level
- @tmp_levels = {}
- @buffer = Hash.new { |h,k| h[k] = [] }
- @auto_flushing = 1
- @guard = Mutex.new
-
- if log.respond_to?(:write)
- @log = log
- elsif File.exist?(log)
- @log = open_log(log, (File::WRONLY | File::APPEND))
- else
- FileUtils.mkdir_p(File.dirname(log))
- @log = open_log(log, (File::WRONLY | File::APPEND | File::CREAT))
+ @log_dest = log
+
+ unless log.respond_to?(:write)
+ unless File.exist?(File.dirname(log))
+ ActiveSupport::Deprecation.warn(<<-eowarn)
+Automatic directory creation for '#{log}' is deprecated. Please make sure the directory for your log file exists before creating the logger.
+ eowarn
+ FileUtils.mkdir_p(File.dirname(log))
+ end
end
+
+ @log = open_logfile log
end
def open_log(log, mode)
@@ -67,20 +66,18 @@ module ActiveSupport
open_log.sync = true
end
end
+ deprecate :open_log
def level
- @tmp_levels[Thread.current] || @level
+ @log.level
+ end
+
+ def level=(l)
+ @log.level = l
end
def add(severity, message = nil, progname = nil, &block)
- return if level > severity
- message = (message || (block && block.call) || progname).to_s
- # If a newline is necessary then create a new message ending with a newline.
- # Ensures that the original message is not mutated.
- message = "#{message}\n" unless message[-1] == ?\n
- buffer << message
- auto_flush
- message
+ @log.add(severity, message, progname, &block)
end
# Dynamically add methods such as:
@@ -104,62 +101,25 @@ module ActiveSupport
# never auto-flush. If you turn auto-flushing off, be sure to regularly
# flush the log yourself -- it will eat up memory until you do.
def auto_flushing=(period)
- @auto_flushing =
- case period
- when true; 1
- when false, nil, 0; MAX_BUFFER_SIZE
- when Integer; period
- else raise ArgumentError, "Unrecognized auto_flushing period: #{period.inspect}"
- end
end
+ deprecate :auto_flushing=
def flush
- @guard.synchronize do
- write_buffer(buffer)
-
- # Important to do this even if buffer was empty or else @buffer will
- # accumulate empty arrays for each request where nothing was logged.
- clear_buffer
+ end
+ deprecate :flush
- # Clear buffers associated with dead threads or else spawned threads
- # that don't call flush will result in a memory leak.
- flush_dead_buffers
- end
+ def respond_to?(method, include_private = false)
+ return false if method.to_s == "flush"
+ super
end
def close
- flush
- @log.close if @log.respond_to?(:close)
- @log = nil
+ @log.close
end
- protected
- def auto_flush
- flush if buffer.size >= @auto_flushing
- end
-
- def buffer
- @buffer[Thread.current]
- end
-
- def clear_buffer
- @buffer.delete(Thread.current)
- end
-
- # Find buffers created by threads that are no longer alive and flush them to the log
- # in order to prevent memory leaks from spawned threads.
- def flush_dead_buffers #:nodoc:
- @buffer.keys.reject{|thread| thread.alive?}.each do |thread|
- buffer = @buffer[thread]
- write_buffer(buffer)
- @buffer.delete(thread)
- end
- end
-
- def write_buffer(buffer)
- buffer.each do |content|
- @log.write(content)
- end
- end
+ private
+ def open_logfile(log)
+ Logger.new log
+ end
end
end
diff --git a/activesupport/lib/active_support/cache.rb b/activesupport/lib/active_support/cache.rb
index 07f5fcdeb3..9711ed6f73 100644
--- a/activesupport/lib/active_support/cache.rb
+++ b/activesupport/lib/active_support/cache.rb
@@ -16,6 +16,7 @@ module ActiveSupport
autoload :FileStore, 'active_support/cache/file_store'
autoload :MemoryStore, 'active_support/cache/memory_store'
autoload :MemCacheStore, 'active_support/cache/mem_cache_store'
+ autoload :NullStore, 'active_support/cache/null_store'
# These options mean something to all cache implementations. Individual cache
# implementations may support additional options.
diff --git a/activesupport/lib/active_support/cache/null_store.rb b/activesupport/lib/active_support/cache/null_store.rb
new file mode 100644
index 0000000000..4427eaafcd
--- /dev/null
+++ b/activesupport/lib/active_support/cache/null_store.rb
@@ -0,0 +1,44 @@
+module ActiveSupport
+ module Cache
+ # A cache store implementation which doesn't actually store anything. Useful in
+ # development and test environments where you don't want caching turned on but
+ # need to go through the caching interface.
+ #
+ # This cache does implement the local cache strategy, so values will actually
+ # be cached inside blocks that utilize this strategy. See
+ # ActiveSupport::Cache::Strategy::LocalCache for more details.
+ class NullStore < Store
+ def initialize(options = nil)
+ super(options)
+ extend Strategy::LocalCache
+ end
+
+ def clear(options = nil)
+ end
+
+ def cleanup(options = nil)
+ end
+
+ def increment(name, amount = 1, options = nil)
+ end
+
+ def decrement(name, amount = 1, options = nil)
+ end
+
+ def delete_matched(matcher, options = nil)
+ end
+
+ protected
+ def read_entry(key, options) # :nodoc:
+ end
+
+ def write_entry(key, entry, options) # :nodoc:
+ true
+ end
+
+ def delete_entry(key, options) # :nodoc:
+ false
+ end
+ end
+ end
+end
diff --git a/activesupport/lib/active_support/core_ext/string/multibyte.rb b/activesupport/lib/active_support/core_ext/string/multibyte.rb
index aae1cfccf2..400db2ce39 100644
--- a/activesupport/lib/active_support/core_ext/string/multibyte.rb
+++ b/activesupport/lib/active_support/core_ext/string/multibyte.rb
@@ -44,7 +44,7 @@ class String
end
end
- def is_utf8? #:nodoc
+ def is_utf8?
case encoding
when Encoding::UTF_8
valid_encoding?
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 7b359a039b..c6d861d124 100644
--- a/activesupport/lib/active_support/core_ext/string/output_safety.rb
+++ b/activesupport/lib/active_support/core_ext/string/output_safety.rb
@@ -6,21 +6,33 @@ class ERB
HTML_ESCAPE = { '&' => '&amp;', '>' => '&gt;', '<' => '&lt;', '"' => '&quot;' }
JSON_ESCAPE = { '&' => '\u0026', '>' => '\u003E', '<' => '\u003C' }
- # A utility method for escaping HTML tag characters.
- # This method is also aliased as <tt>h</tt>.
- #
- # In your ERB templates, use this method to escape any unsafe content. For example:
- # <%=h @person.name %>
- #
- # ==== Example:
- # puts html_escape("is a > 0 & a < 10?")
- # # => is a &gt; 0 &amp; a &lt; 10?
- def html_escape(s)
- s = s.to_s
- if s.html_safe?
- s
- else
- s.gsub(/[&"><]/n) { |special| HTML_ESCAPE[special] }.html_safe
+ # Detect whether 1.9 can transcode with XML escaping.
+ if '"&gt;&lt;&amp;&quot;"' == ('><&"'.encode('utf-8', :xml => :attr) rescue false)
+ # A utility method for escaping HTML tag characters.
+ # This method is also aliased as <tt>h</tt>.
+ #
+ # In your ERB templates, use this method to escape any unsafe content. For example:
+ # <%=h @person.name %>
+ #
+ # ==== Example:
+ # puts html_escape("is a > 0 & a < 10?")
+ # # => is a &gt; 0 &amp; a &lt; 10?
+ def html_escape(s)
+ s = s.to_s
+ if s.html_safe?
+ s
+ else
+ s.encode(s.encoding, :xml => :attr)[1...-1].html_safe
+ end
+ end
+ else
+ def html_escape(s) #:nodoc:
+ s = s.to_s
+ if s.html_safe?
+ s
+ else
+ s.gsub(/[&"><]/n) { |special| HTML_ESCAPE[special] }.html_safe
+ end
end
end
diff --git a/activesupport/lib/active_support/dependencies.rb b/activesupport/lib/active_support/dependencies.rb
index 989c7205fa..43dd22654a 100644
--- a/activesupport/lib/active_support/dependencies.rb
+++ b/activesupport/lib/active_support/dependencies.rb
@@ -87,6 +87,10 @@ module ActiveSupport #:nodoc:
@stack.each(&block)
end
+ def watching?
+ !@watching.empty?
+ end
+
# return a list of new constants found since the last call to watch_namespaces
def new_constants
constants = []
@@ -226,7 +230,7 @@ module ActiveSupport #:nodoc:
end
def load_dependency(file)
- if Dependencies.load?
+ if Dependencies.load? && ActiveSupport::Dependencies.constant_watch_stack.watching?
Dependencies.new_constants_in(Object) { yield }
else
yield
diff --git a/activesupport/lib/active_support/file_update_checker.rb b/activesupport/lib/active_support/file_update_checker.rb
index f76ddff038..a4ad2da137 100644
--- a/activesupport/lib/active_support/file_update_checker.rb
+++ b/activesupport/lib/active_support/file_update_checker.rb
@@ -1,8 +1,28 @@
+require "active_support/core_ext/array/wrap"
+require "active_support/core_ext/array/extract_options"
+
module ActiveSupport
- # This class is responsible to track files and invoke the given block
- # whenever one of these files are changed. For example, this class
- # is used by Rails to reload the I18n framework whenever they are
- # changed upon a new request.
+ # \FileUpdateChecker specifies the API used by Rails to watch files
+ # and control reloading. The API depends on four methods:
+ #
+ # * +initialize+ which expects two parameters and one block as
+ # described below;
+ #
+ # * +updated?+ which returns a boolean if there were updates in
+ # the filesystem or not;
+ #
+ # * +execute+ which executes the given block on initialization
+ # and updates the counter to the latest timestamp;
+ #
+ # * +execute_if_updated+ which just executes the block if it was updated;
+ #
+ # After initialization, a call to +execute_if_updated+ must execute
+ # the block only if there was really a change in the filesystem.
+ #
+ # == Examples
+ #
+ # This class is used by Rails to reload the I18n framework whenever
+ # they are changed upon a new request.
#
# i18n_reloader = ActiveSupport::FileUpdateChecker.new(paths) do
# I18n.reload!
@@ -13,24 +33,89 @@ module ActiveSupport
# end
#
class FileUpdateChecker
- attr_reader :paths, :last_update_at
-
- def initialize(paths, calculate=false, &block)
- @paths = paths
+ # It accepts two parameters on initialization. The first is an array
+ # of files and the second is an optional hash of directories. The hash must
+ # have directories as keys and the value is an array of extensions to be
+ # watched under that directory.
+ #
+ # This method must also receive a block that will be called once a path changes.
+ #
+ # == Implementation details
+ #
+ # This particular implementation checks for added and updated files,
+ # but not removed files. Directories lookup are compiled to a glob for
+ # performance. Therefore, while someone can add new files to the +files+
+ # array after initialization (and parts of Rails do depend on this feature),
+ # adding new directories after initialization is not allowed.
+ #
+ # Notice that other objects that implements FileUpdateChecker API may
+ # not even allow new files to be added after initialization. If this
+ # is the case, we recommend freezing the +files+ after initialization to
+ # avoid changes that won't make effect.
+ def initialize(files, dirs={}, &block)
+ @files = files
+ @glob = compile_glob(dirs)
@block = block
- @last_update_at = calculate ? updated_at : nil
+ @updated_at = nil
+ @last_update_at = updated_at
+ end
+
+ # Check if any of the entries were updated. If so, the updated_at
+ # value is cached until the block is executed via +execute+ or +execute_if_updated+
+ def updated?
+ current_updated_at = updated_at
+ if @last_update_at < current_updated_at
+ @updated_at = updated_at
+ true
+ else
+ false
+ end
end
- def updated_at
- paths.map { |path| File.mtime(path) }.max
+ # Executes the given block and updates the counter to latest timestamp.
+ def execute
+ @last_update_at = updated_at
+ @block.call
+ ensure
+ @updated_at = nil
end
+ # Execute the block given if updated.
def execute_if_updated
- current_update_at = self.updated_at
- if @last_update_at != current_update_at
- @last_update_at = current_update_at
- @block.call
+ if updated?
+ execute
+ true
+ else
+ false
+ end
+ end
+
+ private
+
+ def updated_at #:nodoc:
+ @updated_at || begin
+ all = []
+ all.concat @files.select { |f| File.exists?(f) }
+ all.concat Dir[@glob] if @glob
+ all.map { |path| File.mtime(path) }.max || Time.at(0)
end
end
+
+ def compile_glob(hash) #:nodoc:
+ hash.freeze # Freeze so changes aren't accidently pushed
+ return if hash.empty?
+
+ globs = []
+ hash.each do |key, value|
+ globs << "#{key}/**/*#{compile_ext(value)}"
+ end
+ "{#{globs.join(",")}}"
+ end
+
+ def compile_ext(array) #:nodoc:
+ array = Array.wrap(array)
+ return if array.empty?
+ ".{#{array.join(",")}}"
+ 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
index 636f019cd5..674e4acfd6 100644
--- a/activesupport/lib/active_support/hash_with_indifferent_access.rb
+++ b/activesupport/lib/active_support/hash_with_indifferent_access.rb
@@ -16,6 +16,10 @@ module ActiveSupport
dup
end
+ def nested_under_indifferent_access
+ self
+ end
+
def initialize(constructor = {})
if constructor.is_a?(Hash)
super()
diff --git a/activesupport/lib/active_support/i18n_railtie.rb b/activesupport/lib/active_support/i18n_railtie.rb
index 4c59fe9ac9..bbeb8d82c6 100644
--- a/activesupport/lib/active_support/i18n_railtie.rb
+++ b/activesupport/lib/active_support/i18n_railtie.rb
@@ -10,14 +10,19 @@ module I18n
config.i18n.fallbacks = ActiveSupport::OrderedOptions.new
def self.reloader
- @reloader ||= ActiveSupport::FileUpdateChecker.new([]){ I18n.reload! }
+ @reloader ||= ActiveSupport::FileUpdateChecker.new(reloader_paths){ I18n.reload! }
+ end
+
+ def self.reloader_paths
+ @reloader_paths ||= []
end
# Add <tt>I18n::Railtie.reloader</tt> to ActionDispatch callbacks. Since, at this
# point, no path was added to the reloader, I18n.reload! is not triggered
# on to_prepare callbacks. This will only happen on the config.after_initialize
# callback below.
- initializer "i18n.callbacks" do
+ initializer "i18n.callbacks" do |app|
+ app.reloaders << I18n::Railtie.reloader
ActionDispatch::Reloader.to_prepare do
I18n::Railtie.reloader.execute_if_updated
end
@@ -58,8 +63,8 @@ module I18n
init_fallbacks(fallbacks) if fallbacks && validate_fallbacks(fallbacks)
- reloader.paths.concat I18n.load_path
- reloader.execute_if_updated
+ reloader_paths.concat I18n.load_path
+ reloader.execute
@i18n_inited = true
end
diff --git a/activesupport/lib/active_support/inflections.rb b/activesupport/lib/active_support/inflections.rb
index daf2a1e1d9..527cce2594 100644
--- a/activesupport/lib/active_support/inflections.rb
+++ b/activesupport/lib/active_support/inflections.rb
@@ -16,8 +16,8 @@ module ActiveSupport
inflect.plural(/([^aeiouy]|qu)y$/i, '\1ies')
inflect.plural(/(x|ch|ss|sh)$/i, '\1es')
inflect.plural(/(matr|vert|ind)(?:ix|ex)$/i, '\1ices')
- inflect.plural(/([m|l])ouse$/i, '\1ice')
- inflect.plural(/([m|l])ice$/i, '\1ice')
+ inflect.plural(/(m|l)ouse$/i, '\1ice')
+ inflect.plural(/(m|l)ice$/i, '\1ice')
inflect.plural(/^(ox)$/i, '\1en')
inflect.plural(/^(oxen)$/i, '\1')
inflect.plural(/(quiz)$/i, '\1zes')
@@ -35,7 +35,7 @@ module ActiveSupport
inflect.singular(/(s)eries$/i, '\1eries')
inflect.singular(/(m)ovies$/i, '\1ovie')
inflect.singular(/(x|ch|ss|sh)es$/i, '\1')
- inflect.singular(/([m|l])ice$/i, '\1ouse')
+ inflect.singular(/(m|l)ice$/i, '\1ouse')
inflect.singular(/(bus)es$/i, '\1')
inflect.singular(/(o)es$/i, '\1')
inflect.singular(/(shoe)s$/i, '\1')
diff --git a/activesupport/lib/active_support/tagged_logging.rb b/activesupport/lib/active_support/tagged_logging.rb
index 63e8837a07..0f3ac0c132 100644
--- a/activesupport/lib/active_support/tagged_logging.rb
+++ b/activesupport/lib/active_support/tagged_logging.rb
@@ -38,9 +38,9 @@ module ActiveSupport
EOM
end
- def flush(*args)
+ def flush
@tags.delete(Thread.current)
- @logger.flush(*args) if @logger.respond_to?(:flush)
+ @logger.flush if @logger.respond_to?(:flush)
end
def method_missing(method, *args)
diff --git a/activesupport/lib/active_support/whiny_nil.rb b/activesupport/lib/active_support/whiny_nil.rb
index 577db5018e..a065233679 100644
--- a/activesupport/lib/active_support/whiny_nil.rb
+++ b/activesupport/lib/active_support/whiny_nil.rb
@@ -1,21 +1,6 @@
# Extensions to +nil+ which allow for more helpful error messages for people who
# are new to Rails.
#
-# Ruby raises NoMethodError if you invoke a method on an object that does not
-# respond to it:
-#
-# $ ruby -e nil.destroy
-# -e:1: undefined method `destroy' for nil:NilClass (NoMethodError)
-#
-# With these extensions, if the method belongs to the public interface of the
-# classes in NilClass::WHINERS the error message suggests which could be the
-# actual intended class:
-#
-# $ rails runner nil.destroy
-# ...
-# You might have expected an instance of ActiveRecord::Base.
-# ...
-#
# NilClass#id exists in Ruby 1.8 (though it is deprecated). Since +id+ is a fundamental
# method of Active Record models NilClass#id is redefined as well to raise a RuntimeError
# and warn the user. She probably wanted a model database identifier and the 4
@@ -25,36 +10,13 @@
# By default it is on in development and test modes, and it is off in production
# mode.
class NilClass
- METHOD_CLASS_MAP = Hash.new
-
def self.add_whiner(klass)
- methods = klass.public_instance_methods - public_instance_methods
- class_name = klass.name
- methods.each { |method| METHOD_CLASS_MAP[method.to_sym] = class_name }
+ ActiveSupport::Deprecation.warn "NilClass.add_whiner is deprecated and this functionality is " \
+ "removed from Rails versions as it affects Ruby 1.9 performance.", caller
end
- add_whiner ::Array
-
# Raises a RuntimeError when you attempt to call +id+ on +nil+.
def id
raise RuntimeError, "Called id for nil, which would mistakenly be #{object_id} -- if you really wanted the id of nil, use object_id", caller
end
-
- private
- def method_missing(method, *args)
- if klass = METHOD_CLASS_MAP[method]
- raise_nil_warning_for klass, method, caller
- else
- super
- end
- end
-
- # Raises a NoMethodError when you attempt to call a method on +nil+.
- def raise_nil_warning_for(class_name = nil, selector = nil, with_caller = nil)
- message = "You have a nil object when you didn't expect it!"
- message << "\nYou might have expected an instance of #{class_name}." if class_name
- message << "\nThe error occurred while evaluating nil.#{selector}" if selector
-
- raise NoMethodError, message, with_caller || caller
- end
end