diff options
author | Aaron Patterson <aaron.patterson@gmail.com> | 2013-05-17 12:01:30 -0700 |
---|---|---|
committer | Aaron Patterson <aaron.patterson@gmail.com> | 2013-05-17 12:01:30 -0700 |
commit | f796ed6088ab1e347fcfe573a6346d1235453156 (patch) | |
tree | 81c7e25d21ac4dd5f26bd3c6ca91f8d0be7c2cbf /activesupport/lib/active_support | |
parent | eaf54865b1313094ffca16aca1b199394bc58bae (diff) | |
parent | 677b64fcd527529390e232ceedf8fa8bfac224e2 (diff) | |
download | rails-f796ed6088ab1e347fcfe573a6346d1235453156.tar.gz rails-f796ed6088ab1e347fcfe573a6346d1235453156.tar.bz2 rails-f796ed6088ab1e347fcfe573a6346d1235453156.zip |
Merge branch 'master' into stmt
* master: (330 commits)
plugin new missing license spec
let Ruby do the is_a check for us
Mocha 0.14.0 was released with MT5 support. Switch back to gem
Fix named routing regression from 3.2.13
Revert "just call the class method since we know the callbacks are stored at the"
test refactor
Add more data to AR::UnknownAttributeError
Raise when multiple included blocks are defined
Revert "Integration tests support the OPTIONS http method"
restore whitespace in Gemfile between sqlite3 and sprockets
Revert "Add the options method to action_controller testcase."
Check if APP_RAKEFILE is defined
Fix detection of engine in rake db:load_config Broken by d1d7c86d0c8dcb7e75a87644b330c4e9e7d6c1c1
Remove trailing line break
tiny types should only be integers when the length is <= 1. fixes #10620
add failing test exposing mysql adapter tinyint bug
require things we need
Revert "Merge pull request #10600 from aditya-kapoor/code_refactor"
just call the class method since we know the callbacks are stored at the class level
this variable is used, so we don't have to use double assignments
...
Diffstat (limited to 'activesupport/lib/active_support')
39 files changed, 556 insertions, 436 deletions
diff --git a/activesupport/lib/active_support/cache.rb b/activesupport/lib/active_support/cache.rb index 6c220ae625..b1ab5570a8 100644 --- a/activesupport/lib/active_support/cache.rb +++ b/activesupport/lib/active_support/cache.rb @@ -352,6 +352,34 @@ module ActiveSupport results end + # Fetches data from the cache, using the given keys. If there is data in + # the cache with the given keys, then that data is returned. Otherwise, + # the supplied block is called for each key for which there was no data, + # and the result will be written to the cache and returned. + # + # Options are passed to the underlying cache implementation. + # + # Returns an array with the data for each of the names. For example: + # + # cache.write("bim", "bam") + # cache.fetch_multi("bim", "boom") {|key| key * 2 } + # #=> ["bam", "boomboom"] + # + def fetch_multi(*names) + options = names.extract_options! + options = merged_options(options) + + results = read_multi(*names, options) + + names.map do |name| + results.fetch(name) do + value = yield name + write(name, value, options) + value + end + end + end + # Writes the value to the cache, with the key. # # Options are passed to the underlying cache implementation. diff --git a/activesupport/lib/active_support/callbacks.rb b/activesupport/lib/active_support/callbacks.rb index 893c2500d7..bbfa74f98d 100644 --- a/activesupport/lib/active_support/callbacks.rb +++ b/activesupport/lib/active_support/callbacks.rb @@ -1,9 +1,9 @@ -require 'thread_safe' require 'active_support/concern' require 'active_support/descendants_tracker' require 'active_support/core_ext/class/attribute' require 'active_support/core_ext/kernel/reporting' require 'active_support/core_ext/kernel/singleton_class' +require 'thread' module ActiveSupport # Callbacks are code hooks that are run at key points in an object's lifecycle. @@ -76,8 +76,14 @@ module ActiveSupport # save # end def run_callbacks(kind, &block) - runner_name = self.class.__define_callbacks(kind, self) - send(runner_name, &block) + cbs = send("_#{kind}_callbacks") + if cbs.empty? + yield if block_given? + else + runner = cbs.compile + e = Filters::Environment.new(self, false, nil, block) + runner.call(e).value + end end private @@ -88,175 +94,306 @@ module ActiveSupport def halted_callback_hook(filter) end - class Callback #:nodoc:# - @@_callback_sequence = 0 + module Filters + Environment = Struct.new(:target, :halted, :value, :run_block) - attr_accessor :chain, :filter, :kind, :options, :klass, :raw_filter + class End + def call(env) + block = env.run_block + env.value = !env.halted && (!block || block.call) + env + end + end + ENDING = End.new - def initialize(chain, filter, kind, options, klass) - @chain, @kind, @klass = chain, kind, klass - deprecate_per_key_option(options) - normalize_options!(options) + class Before + def self.build(next_callback, user_callback, user_conditions, chain_config, filter) + halted_lambda = chain_config[:terminator] - @raw_filter, @options = filter, options - @filter = _compile_filter(filter) - recompile_options! - end + if chain_config.key?(:terminator) && user_conditions.any? + halting_and_conditional(next_callback, user_callback, user_conditions, halted_lambda, filter) + elsif chain_config.key? :terminator + halting(next_callback, user_callback, halted_lambda, filter) + elsif user_conditions.any? + conditional(next_callback, user_callback, user_conditions) + else + simple next_callback, user_callback + end + end + + private + + def self.halting_and_conditional(next_callback, user_callback, user_conditions, halted_lambda, filter) + lambda { |env| + target = env.target + value = env.value + halted = env.halted - def deprecate_per_key_option(options) - if options[:per_key] - raise NotImplementedError, ":per_key option is no longer supported. Use generic :if and :unless options instead." + if !halted && user_conditions.all? { |c| c.call(target, value) } + result = user_callback.call target, value + env.halted = halted_lambda.call(target, result) + if env.halted + target.send :halted_callback_hook, filter + end + end + next_callback.call env + } end - end - def clone(chain, klass) - obj = super() - obj.chain = chain - obj.klass = klass - obj.options = @options.dup - obj.options[:if] = @options[:if].dup - obj.options[:unless] = @options[:unless].dup - obj - end + def self.halting(next_callback, user_callback, halted_lambda, filter) + lambda { |env| + target = env.target + value = env.value + halted = env.halted + + unless halted + result = user_callback.call target, value + env.halted = halted_lambda.call(target, result) + if env.halted + target.send :halted_callback_hook, filter + end + end + next_callback.call env + } + end - def normalize_options!(options) - options[:if] = Array(options[:if]) - options[:unless] = Array(options[:unless]) - end + def self.conditional(next_callback, user_callback, user_conditions) + lambda { |env| + target = env.target + value = env.value - def name - chain.name + if user_conditions.all? { |c| c.call(target, value) } + user_callback.call target, value + end + next_callback.call env + } + end + + def self.simple(next_callback, user_callback) + lambda { |env| + user_callback.call env.target, env.value + next_callback.call env + } + end end - def next_id - @@_callback_sequence += 1 + class After + def self.build(next_callback, user_callback, user_conditions, chain_config) + if chain_config[:skip_after_callbacks_if_terminated] + if chain_config.key?(:terminator) && user_conditions.any? + halting_and_conditional(next_callback, user_callback, user_conditions) + elsif chain_config.key?(:terminator) + halting(next_callback, user_callback) + elsif user_conditions.any? + conditional next_callback, user_callback, user_conditions + else + simple next_callback, user_callback + end + else + if user_conditions.any? + conditional next_callback, user_callback, user_conditions + else + simple next_callback, user_callback + end + end + end + + private + + def self.halting_and_conditional(next_callback, user_callback, user_conditions) + lambda { |env| + env = next_callback.call env + target = env.target + value = env.value + halted = env.halted + + if !halted && user_conditions.all? { |c| c.call(target, value) } + user_callback.call target, value + end + env + } + end + + def self.halting(next_callback, user_callback) + lambda { |env| + env = next_callback.call env + unless env.halted + user_callback.call env.target, env.value + end + env + } + end + + def self.conditional(next_callback, user_callback, user_conditions) + lambda { |env| + env = next_callback.call env + target = env.target + value = env.value + + if user_conditions.all? { |c| c.call(target, value) } + user_callback.call target, value + end + env + } + end + + def self.simple(next_callback, user_callback) + lambda { |env| + env = next_callback.call env + user_callback.call env.target, env.value + env + } + end end - def matches?(_kind, _filter) - if @_is_object_filter - _filter_matches = @filter.to_s.start_with?(_method_name_for_object_filter(_kind, _filter, false)) - else - _filter_matches = (@filter == _filter) + class Around + def self.build(next_callback, user_callback, user_conditions, chain_config) + if chain_config.key?(:terminator) && user_conditions.any? + halting_and_conditional(next_callback, user_callback, user_conditions) + elsif chain_config.key? :terminator + halting(next_callback, user_callback) + elsif user_conditions.any? + conditional(next_callback, user_callback, user_conditions) + else + simple(next_callback, user_callback) + end + end + + private + + def self.halting_and_conditional(next_callback, user_callback, user_conditions) + lambda { |env| + target = env.target + value = env.value + halted = env.halted + + if !halted && user_conditions.all? { |c| c.call(target, value) } + user_callback.call(target, value) { + env = next_callback.call env + env.value + } + env + else + next_callback.call env + end + } + end + + def self.halting(next_callback, user_callback) + lambda { |env| + target = env.target + value = env.value + + unless env.halted + user_callback.call(target, value) { + env = next_callback.call env + env.value + } + env + else + next_callback.call env + end + } + end + + def self.conditional(next_callback, user_callback, user_conditions) + lambda { |env| + target = env.target + value = env.value + + if user_conditions.all? { |c| c.call(target, value) } + user_callback.call(target, value) { + env = next_callback.call env + env.value + } + env + else + next_callback.call env + end + } + end + + def self.simple(next_callback, user_callback) + lambda { |env| + user_callback.call(env.target, env.value) { + env = next_callback.call env + env.value + } + env + } end + end + end - @kind == _kind && _filter_matches + class Callback #:nodoc:# + def self.build(chain, filter, kind, options) + new chain.name, filter, kind, options, chain.config end - def duplicates?(other) - matches?(other.kind, other.filter) + attr_accessor :kind, :name + attr_reader :chain_config + + def initialize(name, filter, kind, options, chain_config) + @chain_config = chain_config + @name = name + @kind = kind + @filter = filter + @key = compute_identifier filter + @if = Array(options[:if]) + @unless = Array(options[:unless]) end - def _update_filter(filter_options, new_options) - filter_options[:if].concat(Array(new_options[:unless])) if new_options.key?(:unless) - filter_options[:unless].concat(Array(new_options[:if])) if new_options.key?(:if) + def filter; @key; end + def raw_filter; @filter; end + + def merge(chain, new_options) + options = { + :if => @if.dup, + :unless => @unless.dup + } + + options[:if].concat Array(new_options.fetch(:unless, [])) + options[:unless].concat Array(new_options.fetch(:if, [])) + + self.class.build chain, @filter, @kind, options end - def recompile!(_options) - deprecate_per_key_option(_options) - _update_filter(self.options, _options) + def matches?(_kind, _filter) + @kind == _kind && filter == _filter + end - recompile_options! + def duplicates?(other) + case @filter + when Symbol, String + matches?(other.kind, other.filter) + else + false + end end # Wraps code with filter - def apply(code) - case @kind + def apply(next_callback) + user_conditions = conditions_lambdas + user_callback = make_lambda @filter + + case kind when :before - <<-RUBY_EVAL - if !halted && #{@compiled_options} - # This double assignment is to prevent warnings in 1.9.3 as - # the `result` variable is not always used except if the - # terminator code refers to it. - result = result = #{@filter} - halted = (#{chain.config[:terminator]}) - if halted - halted_callback_hook(#{@raw_filter.inspect.inspect}) - end - end - #{code} - RUBY_EVAL + Filters::Before.build(next_callback, user_callback, user_conditions, chain_config, @filter) when :after - <<-RUBY_EVAL - #{code} - if #{!chain.config[:skip_after_callbacks_if_terminated] || "!halted"} && #{@compiled_options} - #{@filter} - end - RUBY_EVAL + Filters::After.build(next_callback, user_callback, user_conditions, chain_config) when :around - name = define_conditional_callback - <<-RUBY_EVAL - #{name}(halted) do - #{code} - value - end - RUBY_EVAL + Filters::Around.build(next_callback, user_callback, user_conditions, chain_config) end end private - # Compile around filters with conditions into proxy methods - # that contain the conditions. - # - # For `set_callback :save, :around, :filter_name, if: :condition': - # - # def _conditional_callback_save_17 - # if condition - # filter_name do - # yield self - # end - # else - # yield self - # end - # end - def define_conditional_callback - name = "_conditional_callback_#{@kind}_#{next_id}" - @klass.class_eval <<-RUBY_EVAL, __FILE__, __LINE__ + 1 - def #{name}(halted) - if #{@compiled_options} && !halted - #{@filter} do - yield self - end - else - yield self - end - end - RUBY_EVAL - name - end - - # Options support the same options as filters themselves (and support - # symbols, string, procs, and objects), so compile a conditional - # expression based on the options. - def recompile_options! - conditions = ["true"] - - unless options[:if].empty? - conditions << Array(_compile_filter(options[:if])) - end - - unless options[:unless].empty? - conditions << Array(_compile_filter(options[:unless])).map {|f| "!#{f}"} - end - - @compiled_options = conditions.flatten.join(" && ") - end - - def _method_name_for_object_filter(kind, filter, append_next_id = true) - class_name = filter.kind_of?(Class) ? filter.to_s : filter.class.to_s - class_name.gsub!(/<|>|#/, '') - class_name.gsub!(/\/|:/, "_") - - method_name = "_callback_#{kind}_#{class_name}" - method_name << "_#{next_id}" if append_next_id - method_name + def invert_lambda(l) + lambda { |*args, &blk| !l.call(*args, &blk) } end # Filters support: # - # Arrays:: Used in conditions. This is used to specify - # multiple conditions. Used internally to - # merge conditions from skip_* filters. # Symbols:: A method to call. # Strings:: Some content to evaluate. # Procs:: A proc to call with the object. @@ -265,90 +402,105 @@ module ActiveSupport # All of these objects are compiled into methods and handled # the same after this point: # - # Arrays:: Merged together into a single filter. # Symbols:: Already methods. # Strings:: class_eval'ed into methods. # Procs:: define_method'ed into methods. # Objects:: # a method is created that calls the before_foo method # on the object. - def _compile_filter(filter) - @_is_object_filter = false - + def make_lambda(filter) case filter - when Array - filter.map {|f| _compile_filter(f)} when Symbol - filter + lambda { |target, _, &blk| target.send filter, &blk } when String - "(#{filter})" - when Proc - method_name = "_callback_#{@kind}_#{next_id}" - @klass.send(:define_method, method_name, &filter) - return method_name if filter.arity <= 0 + l = eval "lambda { |value| #{filter} }" + lambda { |target, value| target.instance_exec(value, &l) } + when ::Proc + if filter.arity > 1 + return lambda { |target, _, &block| + raise ArgumentError unless block + target.instance_exec(target, block, &filter) + } + end - method_name << (filter.arity == 1 ? "(self)" : " self, Proc.new ") + if filter.arity <= 0 + lambda { |target, _| target.instance_exec(&filter) } + else + lambda { |target, _| target.instance_exec(target, &filter) } + end else - method_name = _method_name_for_object_filter(kind, filter) - @_is_object_filter = true - @klass.send(:define_method, "#{method_name}_object") { filter } + scopes = Array(chain_config[:scope]) + method_to_call = scopes.map{ |s| public_send(s) }.join("_") - _normalize_legacy_filter(kind, filter) - scopes = Array(chain.config[:scope]) - method_to_call = scopes.map{ |s| s.is_a?(Symbol) ? send(s) : s }.join("_") - - @klass.class_eval <<-RUBY_EVAL, __FILE__, __LINE__ + 1 - def #{method_name}(&blk) - #{method_name}_object.send(:#{method_to_call}, self, &blk) - end - RUBY_EVAL - - method_name + lambda { |target, _, &blk| + filter.public_send method_to_call, target, &blk + } end end - def _normalize_legacy_filter(kind, filter) - if !filter.respond_to?(kind) && filter.respond_to?(:filter) - message = "Filter object with #filter method is deprecated. Define method corresponding " \ - "to filter type (#before, #after or #around)." - ActiveSupport::Deprecation.warn message - filter.singleton_class.class_eval <<-RUBY_EVAL, __FILE__, __LINE__ + 1 - def #{kind}(context, &block) filter(context, &block) end - RUBY_EVAL - elsif filter.respond_to?(:before) && filter.respond_to?(:after) && kind == :around && !filter.respond_to?(:around) - message = "Filter object with #before and #after methods is deprecated. Define #around method instead." - ActiveSupport::Deprecation.warn message - def filter.around(context) - should_continue = before(context) - yield if should_continue - after(context) - end + def compute_identifier(filter) + case filter + when String, ::Proc + filter.object_id + else + filter end end + + def conditions_lambdas + @if.map { |c| make_lambda c } + + @unless.map { |c| invert_lambda make_lambda c } + end end # An Array with a compile method. - class CallbackChain < Array #:nodoc:# + class CallbackChain #:nodoc:# + include Enumerable + attr_reader :name, :config def initialize(name, config) @name = name @config = { - :terminator => "false", :scope => [ :kind ] }.merge!(config) + @chain = [] + @callbacks = nil + @mutex = Mutex.new + end + + def each(&block); @chain.each(&block); end + def index(o); @chain.index(o); end + def empty?; @chain.empty?; end + + def insert(index, o) + @callbacks = nil + @chain.insert(index, o) + end + + def delete(o) + @callbacks = nil + @chain.delete(o) + end + + def clear + @callbacks = nil + @chain.clear + self + end + + def initialize_copy(other) + @callbacks = nil + @chain = other.chain.dup + @mutex = Mutex.new end def compile - method = ["value = nil", "halted = false"] - callbacks = "value = !halted && (!block_given? || yield)" - reverse_each do |callback| - callbacks = callback.apply(callbacks) + @callbacks || @mutex.synchronize do + @callbacks ||= @chain.reverse.inject(Filters::ENDING) do |chain, callback| + callback.apply chain + end end - method << callbacks - - method << "value" - method.join("\n") end def append(*callbacks) @@ -359,69 +511,45 @@ module ActiveSupport callbacks.each { |c| prepend_one(c) } end + protected + def chain; @chain; end + private def append_one(callback) + @callbacks = nil remove_duplicates(callback) - push(callback) + @chain.push(callback) end def prepend_one(callback) + @callbacks = nil remove_duplicates(callback) - unshift(callback) + @chain.unshift(callback) end def remove_duplicates(callback) - delete_if { |c| callback.duplicates?(c) } + @callbacks = nil + @chain.delete_if { |c| callback.duplicates?(c) } end end module ClassMethods - # This method defines callback chain method for the given kind - # if it was not yet defined. - # This generated method plays caching role. - def __define_callbacks(kind, object) #:nodoc: - name = __callback_runner_name(kind) - unless object.respond_to?(name, true) - str = object.send("_#{kind}_callbacks").compile - class_eval <<-RUBY_EVAL, __FILE__, __LINE__ + 1 - def #{name}() #{str} end - protected :#{name} - RUBY_EVAL - end - name - end - - def __reset_runner(symbol) - name = __callback_runner_name(symbol) - undef_method(name) if method_defined?(name) - end - - def __callback_runner_name_cache - @__callback_runner_name_cache ||= ThreadSafe::Cache.new {|cache, kind| cache[kind] = __generate_callback_runner_name(kind) } - end - - def __generate_callback_runner_name(kind) - "_run__#{self.name.hash.abs}__#{kind}__callbacks" - end - - def __callback_runner_name(kind) - __callback_runner_name_cache[kind] - end - - # This is used internally to append, prepend and skip callbacks to the - # CallbackChain. - def __update_callbacks(name, filters = [], block = nil) #:nodoc: + def normalize_callback_params(name, filters, block) # :nodoc: type = CALLBACK_FILTER_TYPES.include?(filters.first) ? filters.shift : :before options = filters.last.is_a?(Hash) ? filters.pop : {} filters.unshift(block) if block + [type, filters, options.dup] + end + # This is used internally to append, prepend and skip callbacks to the + # CallbackChain. + def __update_callbacks(name) #:nodoc: ([self] + ActiveSupport::DescendantsTracker.descendants(self)).reverse.each do |target| - chain = target.send("_#{name}_callbacks") - yield target, chain.dup, type, filters, options - target.__reset_runner(name) + chain = target.get_callbacks name + yield target, chain.dup end end @@ -461,16 +589,15 @@ module ActiveSupport # * <tt>:prepend</tt> - If +true+, the callback will be prepended to the # existing chain rather than appended. def set_callback(name, *filter_list, &block) - mapped = nil - - __update_callbacks(name, filter_list, block) do |target, chain, type, filters, options| - mapped ||= filters.map do |filter| - Callback.new(chain, filter, type, options.dup, self) - end + type, filters, options = normalize_callback_params(name, filter_list, block) + self_chain = get_callbacks name + mapped = filters.map do |filter| + Callback.build(self_chain, filter, type, options) + end + __update_callbacks(name) do |target, chain| options[:prepend] ? chain.prepend(*mapped) : chain.append(*mapped) - - target.send("_#{name}_callbacks=", chain) + target.set_callbacks name, chain end end @@ -482,36 +609,34 @@ module ActiveSupport # skip_callback :validate, :before, :check_membership, if: -> { self.age > 18 } # end def skip_callback(name, *filter_list, &block) - __update_callbacks(name, filter_list, block) do |target, chain, type, filters, options| + type, filters, options = normalize_callback_params(name, filter_list, block) + + __update_callbacks(name) do |target, chain| filters.each do |filter| filter = chain.find {|c| c.matches?(type, filter) } if filter && options.any? - new_filter = filter.clone(chain, self) + new_filter = filter.merge(chain, options) chain.insert(chain.index(filter), new_filter) - new_filter.recompile!(options) end chain.delete(filter) end - target.send("_#{name}_callbacks=", chain) + target.set_callbacks name, chain end end # Remove all set callbacks for the given event. def reset_callbacks(symbol) - callbacks = send("_#{symbol}_callbacks") + callbacks = get_callbacks symbol ActiveSupport::DescendantsTracker.descendants(self).each do |target| - chain = target.send("_#{symbol}_callbacks").dup + chain = target.get_callbacks(symbol).dup callbacks.each { |c| chain.delete(c) } - target.send("_#{symbol}_callbacks=", chain) - target.__reset_runner(symbol) + target.set_callbacks symbol, chain end - self.send("_#{symbol}_callbacks=", callbacks.dup.clear) - - __reset_runner(symbol) + self.set_callbacks symbol, callbacks.dup.clear end # Define sets of events in the object lifecycle that support callbacks. @@ -583,11 +708,28 @@ module ActiveSupport # would call <tt>Audit#save</tt>. def define_callbacks(*callbacks) config = callbacks.last.is_a?(Hash) ? callbacks.pop : {} + if config.key?(:terminator) && String === config[:terminator] + ActiveSupport::Deprecation.warn "String based terminators are deprecated, please use a lambda" + value = config[:terminator] + l = class_eval "lambda { |result| #{value} }", __FILE__, __LINE__ + config[:terminator] = lambda { |target, result| target.instance_exec(result, &l) } + end + callbacks.each do |callback| class_attribute "_#{callback}_callbacks" - send("_#{callback}_callbacks=", CallbackChain.new(callback, config)) + set_callbacks callback, CallbackChain.new(callback, config) end end + + protected + + def get_callbacks(name) + send "_#{name}_callbacks" + end + + def set_callbacks(name, callbacks) + send "_#{name}_callbacks=", callbacks + end end end end diff --git a/activesupport/lib/active_support/concern.rb b/activesupport/lib/active_support/concern.rb index eeeba60839..b6ae86b583 100644 --- a/activesupport/lib/active_support/concern.rb +++ b/activesupport/lib/active_support/concern.rb @@ -98,6 +98,12 @@ module ActiveSupport # include Bar # works, Bar takes care now of its dependencies # end module Concern + class MultipleIncludedBlocks < StandardError #:nodoc: + def initialize + super "Cannot define multiple 'included' blocks for a Concern" + end + end + def self.extended(base) #:nodoc: base.instance_variable_set("@_dependencies", []) end @@ -117,6 +123,8 @@ module ActiveSupport def included(base = nil, &block) if base.nil? + raise MultipleIncludedBlocks if instance_variable_defined?("@_included_block") + @_included_block = block else super diff --git a/activesupport/lib/active_support/core_ext/array/grouping.rb b/activesupport/lib/active_support/core_ext/array/grouping.rb index 640e6e9328..dbddc7a7b4 100644 --- a/activesupport/lib/active_support/core_ext/array/grouping.rb +++ b/activesupport/lib/active_support/core_ext/array/grouping.rb @@ -25,7 +25,7 @@ class Array # 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) + collection = dup.concat(Array.new(padding, fill_with)) end if block_given? diff --git a/activesupport/lib/active_support/core_ext/array/wrap.rb b/activesupport/lib/active_support/core_ext/array/wrap.rb index 1245768870..152eb02218 100644 --- a/activesupport/lib/active_support/core_ext/array/wrap.rb +++ b/activesupport/lib/active_support/core_ext/array/wrap.rb @@ -15,12 +15,12 @@ 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 - # such a +nil+ right away. + # +nil+ 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, though special-cases +nil+ to return an empty array. + # * It does not call +to_a+ on the argument, but returns an empty array if argument is +nil+. # - # The last point is particularly worth comparing for some enumerables: + # The second point is easily explained with some enumerables: # # Array(foo: :bar) # => [[:foo, :bar]] # Array.wrap(foo: :bar) # => [{:foo=>:bar}] @@ -29,10 +29,10 @@ class Array # # [*object] # - # which for +nil+ returns <tt>[]</tt>, and calls to <tt>Array(object)</tt> otherwise. + # which returns <tt>[]</tt> for +nil+, but calls to <tt>Array(object)</tt> otherwise. # - # Thus, in this case the behavior may be different for +nil+, and the differences with - # <tt>Kernel#Array</tt> explained above apply to the rest of <tt>object</tt>s. + # The differences with <tt>Kernel#Array</tt> explained above + # apply to the rest of <tt>object</tt>s. def self.wrap(object) if object.nil? [] diff --git a/activesupport/lib/active_support/core_ext/class/attribute.rb b/activesupport/lib/active_support/core_ext/class/attribute.rb index 6d49b7b6e1..6fa9967a28 100644 --- a/activesupport/lib/active_support/core_ext/class/attribute.rb +++ b/activesupport/lib/active_support/core_ext/class/attribute.rb @@ -71,7 +71,7 @@ class Class def class_attribute(*attrs) options = attrs.extract_options! # double assignment is used to avoid "assigned but unused variable" warning - instance_reader = instance_reader = options.fetch(:instance_accessor, true) && options.fetch(:instance_reader, true) + instance_reader = options.fetch(:instance_accessor, true) && options.fetch(:instance_reader, true) instance_writer = options.fetch(:instance_accessor, true) && options.fetch(:instance_writer, true) instance_predicate = options.fetch(:instance_predicate, true) diff --git a/activesupport/lib/active_support/core_ext/date.rb b/activesupport/lib/active_support/core_ext/date.rb index 5f13f5f70f..465fedda80 100644 --- a/activesupport/lib/active_support/core_ext/date.rb +++ b/activesupport/lib/active_support/core_ext/date.rb @@ -2,5 +2,4 @@ require 'active_support/core_ext/date/acts_like' require 'active_support/core_ext/date/calculations' require 'active_support/core_ext/date/conversions' require 'active_support/core_ext/date/zones' -require 'active_support/core_ext/date/infinite_comparable' diff --git a/activesupport/lib/active_support/core_ext/date/conversions.rb b/activesupport/lib/active_support/core_ext/date/conversions.rb index cdf606f28c..0637fe4929 100644 --- a/activesupport/lib/active_support/core_ext/date/conversions.rb +++ b/activesupport/lib/active_support/core_ext/date/conversions.rb @@ -1,7 +1,6 @@ require 'date' require 'active_support/inflector/methods' require 'active_support/core_ext/date/zones' -require 'active_support/core_ext/module/remove_method' class Date DATE_FORMATS = { diff --git a/activesupport/lib/active_support/core_ext/date/infinite_comparable.rb b/activesupport/lib/active_support/core_ext/date/infinite_comparable.rb deleted file mode 100644 index ca5d793942..0000000000 --- a/activesupport/lib/active_support/core_ext/date/infinite_comparable.rb +++ /dev/null @@ -1,5 +0,0 @@ -require 'active_support/core_ext/infinite_comparable' - -class Date - include InfiniteComparable -end diff --git a/activesupport/lib/active_support/core_ext/date_time.rb b/activesupport/lib/active_support/core_ext/date_time.rb index 024af91738..e8a27b9f38 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/calculations' require 'active_support/core_ext/date_time/conversions' require 'active_support/core_ext/date_time/zones' -require 'active_support/core_ext/date_time/infinite_comparable' 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 9f0864d9bb..937567440b 100644 --- a/activesupport/lib/active_support/core_ext/date_time/calculations.rb +++ b/activesupport/lib/active_support/core_ext/date_time/calculations.rb @@ -1,3 +1,4 @@ +require 'date' require 'active_support/deprecation' class DateTime @@ -154,4 +155,11 @@ class DateTime 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 <=>(other) + super other.to_datetime + end + 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 b7d8414a9d..df07917d19 100644 --- a/activesupport/lib/active_support/core_ext/date_time/conversions.rb +++ b/activesupport/lib/active_support/core_ext/date_time/conversions.rb @@ -1,3 +1,4 @@ +require 'date' require 'active_support/inflector/methods' require 'active_support/core_ext/time/conversions' require 'active_support/core_ext/date_time/calculations' diff --git a/activesupport/lib/active_support/core_ext/date_time/infinite_comparable.rb b/activesupport/lib/active_support/core_ext/date_time/infinite_comparable.rb deleted file mode 100644 index 8a282b19f2..0000000000 --- a/activesupport/lib/active_support/core_ext/date_time/infinite_comparable.rb +++ /dev/null @@ -1,5 +0,0 @@ -require 'active_support/core_ext/infinite_comparable' - -class DateTime - include InfiniteComparable -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 index 6457ffbaf6..01a627f8af 100644 --- a/activesupport/lib/active_support/core_ext/date_time/zones.rb +++ b/activesupport/lib/active_support/core_ext/date_time/zones.rb @@ -1,3 +1,4 @@ +require 'date' require 'active_support/core_ext/time/zones' class DateTime diff --git a/activesupport/lib/active_support/core_ext/hash/diff.rb b/activesupport/lib/active_support/core_ext/hash/diff.rb index 5f3868b5b0..4359213380 100644 --- a/activesupport/lib/active_support/core_ext/hash/diff.rb +++ b/activesupport/lib/active_support/core_ext/hash/diff.rb @@ -1,3 +1,5 @@ +require 'active_support/deprecation' + class Hash # Returns a hash that represents the difference between two hashes. # diff --git a/activesupport/lib/active_support/core_ext/infinite_comparable.rb b/activesupport/lib/active_support/core_ext/infinite_comparable.rb deleted file mode 100644 index b78b2deaad..0000000000 --- a/activesupport/lib/active_support/core_ext/infinite_comparable.rb +++ /dev/null @@ -1,35 +0,0 @@ -require 'active_support/concern' -require 'active_support/core_ext/module/aliasing' -require 'active_support/core_ext/object/try' - -module InfiniteComparable - extend ActiveSupport::Concern - - included do - alias_method_chain :<=>, :infinity - end - - define_method :'<=>_with_infinity' do |other| - if other.class == self.class - public_send :'<=>_without_infinity', other - else - infinite = try(:infinite?) - other_infinite = other.try(:infinite?) - - # inf <=> inf - if infinite && other_infinite - infinite <=> other_infinite - # not_inf <=> inf - elsif other_infinite - -other_infinite - # inf <=> not_inf - elsif infinite - infinite - else - conversion = "to_#{self.class.name.downcase}" - other = other.public_send(conversion) if other.respond_to?(conversion) - public_send :'<=>_without_infinity', other - end - end - end -end diff --git a/activesupport/lib/active_support/core_ext/marshal.rb b/activesupport/lib/active_support/core_ext/marshal.rb index c7a8348b1d..56c79c04bd 100644 --- a/activesupport/lib/active_support/core_ext/marshal.rb +++ b/activesupport/lib/active_support/core_ext/marshal.rb @@ -1,3 +1,5 @@ +require 'active_support/core_ext/module/aliasing' + module Marshal class << self def load_with_autoloading(source) diff --git a/activesupport/lib/active_support/core_ext/numeric.rb b/activesupport/lib/active_support/core_ext/numeric.rb index d5cfc2ece4..a6bc0624be 100644 --- a/activesupport/lib/active_support/core_ext/numeric.rb +++ b/activesupport/lib/active_support/core_ext/numeric.rb @@ -1,4 +1,3 @@ require 'active_support/core_ext/numeric/bytes' require 'active_support/core_ext/numeric/time' require 'active_support/core_ext/numeric/conversions' -require 'active_support/core_ext/numeric/infinite_comparable' diff --git a/activesupport/lib/active_support/core_ext/numeric/infinite_comparable.rb b/activesupport/lib/active_support/core_ext/numeric/infinite_comparable.rb deleted file mode 100644 index b5f1b0487b..0000000000 --- a/activesupport/lib/active_support/core_ext/numeric/infinite_comparable.rb +++ /dev/null @@ -1,9 +0,0 @@ -require 'active_support/core_ext/infinite_comparable' - -class Float - include InfiniteComparable -end - -class BigDecimal - include InfiniteComparable -end diff --git a/activesupport/lib/active_support/core_ext/object/inclusion.rb b/activesupport/lib/active_support/core_ext/object/inclusion.rb index 3fec465ec0..b5671f66d0 100644 --- a/activesupport/lib/active_support/core_ext/object/inclusion.rb +++ b/activesupport/lib/active_support/core_ext/object/inclusion.rb @@ -1,25 +1,15 @@ class Object - # Returns true if this object is included in the argument(s). Argument must be - # any object which responds to +#include?+ or optionally, multiple arguments can be passed in. Usage: + # Returns true if this object is included in the argument. Argument must be + # any object which responds to +#include?+. Usage: # - # characters = ['Konata', 'Kagami', 'Tsukasa'] - # 'Konata'.in?(characters) # => true + # characters = ["Konata", "Kagami", "Tsukasa"] + # "Konata".in?(characters) # => true # - # character = 'Konata' - # character.in?('Konata', 'Kagami', 'Tsukasa') # => true - # - # This will throw an ArgumentError if a single argument is passed in and it doesn't respond + # This will throw an ArgumentError if the argument doesn't respond # to +#include?+. - def in?(*args) - if args.length > 1 - args.include? self - else - another_object = args.first - if another_object.respond_to? :include? - another_object.include? self - else - raise ArgumentError.new 'The single parameter passed to #in? must respond to #include?' - end - end + def in?(another_object) + another_object.include?(self) + rescue NoMethodError + raise ArgumentError.new("The parameter passed to #in? must respond to #include?") end end diff --git a/activesupport/lib/active_support/core_ext/object/to_param.rb b/activesupport/lib/active_support/core_ext/object/to_param.rb index 0d5f3501e5..3b137ce6ae 100644 --- a/activesupport/lib/active_support/core_ext/object/to_param.rb +++ b/activesupport/lib/active_support/core_ext/object/to_param.rb @@ -53,6 +53,6 @@ class Hash def to_param(namespace = nil) collect do |key, value| value.to_query(namespace ? "#{namespace}[#{key}]" : key) - end.sort * '&' + end.sort! * '&' end 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 3af66aaf2f..3a07401c8a 100644 --- a/activesupport/lib/active_support/core_ext/range/include_range.rb +++ b/activesupport/lib/active_support/core_ext/range/include_range.rb @@ -1,3 +1,5 @@ +require 'active_support/core_ext/module/aliasing' + class Range # Extends the default Range#include? to support range comparisons. # (1..5).include?(1..5) # => true diff --git a/activesupport/lib/active_support/core_ext/string/conversions.rb b/activesupport/lib/active_support/core_ext/string/conversions.rb index d2a2db32bb..6691fc0995 100644 --- a/activesupport/lib/active_support/core_ext/string/conversions.rb +++ b/activesupport/lib/active_support/core_ext/string/conversions.rb @@ -15,6 +15,7 @@ class String # "2012-12-13 06:12".to_time # => 2012-12-13 06:12:00 +0100 # "2012-12-13T06:12".to_time # => 2012-12-13 06:12:00 +0100 # "2012-12-13T06:12".to_time(:utc) # => 2012-12-13 05:12:00 UTC + # "12/13/2012".to_time # => ArgumentError: argument out of range def to_time(form = :local) parts = Date._parse(self, false) return if parts.empty? diff --git a/activesupport/lib/active_support/core_ext/string/inflections.rb b/activesupport/lib/active_support/core_ext/string/inflections.rb index 0b506a6030..56e8a5f98d 100644 --- a/activesupport/lib/active_support/core_ext/string/inflections.rb +++ b/activesupport/lib/active_support/core_ext/string/inflections.rb @@ -185,7 +185,7 @@ class String # # Singular names are not handled correctly. # - # 'business'.classify # => "Busines" + # 'business'.classify # => "Business" def classify ActiveSupport::Inflector.classify(self) end diff --git a/activesupport/lib/active_support/core_ext/time.rb b/activesupport/lib/active_support/core_ext/time.rb index af6b589b71..32cffe237d 100644 --- a/activesupport/lib/active_support/core_ext/time.rb +++ b/activesupport/lib/active_support/core_ext/time.rb @@ -3,4 +3,3 @@ require 'active_support/core_ext/time/calculations' require 'active_support/core_ext/time/conversions' require 'active_support/core_ext/time/marshal' require 'active_support/core_ext/time/zones' -require 'active_support/core_ext/time/infinite_comparable' diff --git a/activesupport/lib/active_support/core_ext/time/infinite_comparable.rb b/activesupport/lib/active_support/core_ext/time/infinite_comparable.rb deleted file mode 100644 index 63795885f5..0000000000 --- a/activesupport/lib/active_support/core_ext/time/infinite_comparable.rb +++ /dev/null @@ -1,5 +0,0 @@ -require 'active_support/core_ext/infinite_comparable' - -class Time - include InfiniteComparable -end diff --git a/activesupport/lib/active_support/deprecation/proxy_wrappers.rb b/activesupport/lib/active_support/deprecation/proxy_wrappers.rb index 485dc91063..a03a66b96b 100644 --- a/activesupport/lib/active_support/deprecation/proxy_wrappers.rb +++ b/activesupport/lib/active_support/deprecation/proxy_wrappers.rb @@ -25,7 +25,7 @@ module ActiveSupport end end - # This DeprecatedObjectProxy transforms object to depracated object. + # This DeprecatedObjectProxy transforms object to deprecated object. # # @old_object = DeprecatedObjectProxy.new(Object.new, "Don't use this object anymore!") # @old_object = DeprecatedObjectProxy.new(Object.new, "Don't use this object anymore!", deprecator_instance) @@ -52,7 +52,7 @@ module ActiveSupport end # This DeprecatedInstanceVariableProxy transforms instance variable to - # depracated instance variable. + # deprecated instance variable. # # class Example # def initialize(deprecator) @@ -93,7 +93,7 @@ module ActiveSupport end end - # This DeprecatedConstantProxy transforms constant to depracated constant. + # This DeprecatedConstantProxy transforms constant to deprecated constant. # # OLD_CONST = ActiveSupport::Deprecation::DeprecatedConstantProxy.new('OLD_CONST', 'NEW_CONST') # OLD_CONST = ActiveSupport::Deprecation::DeprecatedConstantProxy.new('OLD_CONST', 'NEW_CONST', deprecator_instance) diff --git a/activesupport/lib/active_support/hash_with_indifferent_access.rb b/activesupport/lib/active_support/hash_with_indifferent_access.rb index 1b20592e4c..bdb8877f55 100644 --- a/activesupport/lib/active_support/hash_with_indifferent_access.rb +++ b/activesupport/lib/active_support/hash_with_indifferent_access.rb @@ -229,7 +229,11 @@ module ActiveSupport # Convert to a regular hash with string keys. def to_hash - Hash.new(default).merge!(self) + _new_hash= {} + each do |key, value| + _new_hash[convert_key(key)] = convert_value(value, true) + end + Hash.new(default).merge!(_new_hash) end protected @@ -237,12 +241,12 @@ module ActiveSupport key.kind_of?(Symbol) ? key.to_s : key end - def convert_value(value) + def convert_value(value, _convert_for_to_hash = false) if value.is_a? Hash - value.nested_under_indifferent_access + _convert_for_to_hash ? value.to_hash : value.nested_under_indifferent_access elsif value.is_a?(Array) value = value.dup if value.frozen? - value.map! { |e| convert_value(e) } + value.map! { |e| convert_value(e, _convert_for_to_hash) } else value end diff --git a/activesupport/lib/active_support/i18n.rb b/activesupport/lib/active_support/i18n.rb index 22521a8e93..6cc98191d4 100644 --- a/activesupport/lib/active_support/i18n.rb +++ b/activesupport/lib/active_support/i18n.rb @@ -1,13 +1,13 @@ +require 'active_support/core_ext/hash/deep_merge' +require 'active_support/core_ext/hash/except' +require 'active_support/core_ext/hash/slice' begin - require 'active_support/core_ext/hash/deep_merge' - require 'active_support/core_ext/hash/except' - require 'active_support/core_ext/hash/slice' require 'i18n' - require 'active_support/lazy_load_hooks' rescue LoadError => e $stderr.puts "The i18n gem is not available. Please add it to your Gemfile and run bundle install" raise e end +require 'active_support/lazy_load_hooks' ActiveSupport.run_load_hooks(:i18n) I18n.load_path << "#{File.dirname(__FILE__)}/locale/en.yml" diff --git a/activesupport/lib/active_support/json/decoding.rb b/activesupport/lib/active_support/json/decoding.rb index a4a32b2ad0..30833a4cb1 100644 --- a/activesupport/lib/active_support/json/decoding.rb +++ b/activesupport/lib/active_support/json/decoding.rb @@ -1,6 +1,6 @@ require 'active_support/core_ext/module/attribute_accessors' require 'active_support/core_ext/module/delegation' -require 'multi_json' +require 'json' module ActiveSupport # Look for and parse json strings that look like ISO 8601 times. @@ -13,8 +13,8 @@ module ActiveSupport # # ActiveSupport::JSON.decode("{\"team\":\"rails\",\"players\":\"36\"}") # => {"team" => "rails", "players" => "36"} - def decode(json, options ={}) - data = MultiJson.load(json, options) + def decode(json, proc = nil, options = {}) + data = ::JSON.load(json, proc, options) if ActiveSupport.parse_json_times convert_dates_from(data) else @@ -22,23 +22,6 @@ module ActiveSupport end end - def engine - MultiJson.adapter - end - alias :backend :engine - - def engine=(name) - MultiJson.use(name) - end - alias :backend= :engine= - - def with_backend(name) - old_backend, self.backend = backend, name - yield - ensure - self.backend = old_backend - end - # Returns the class of the error that will be raised when there is an # error in decoding JSON. Using this method means you won't directly # depend on the ActiveSupport's JSON implementation, in case it changes @@ -50,7 +33,7 @@ module ActiveSupport # Rails.logger.warn("Attempted to decode invalid JSON: #{some_string}") # end def parse_error - MultiJson::DecodeError + ::JSON::ParserError end private diff --git a/activesupport/lib/active_support/json/encoding.rb b/activesupport/lib/active_support/json/encoding.rb index 9bf1ea35b3..7f8b41d218 100644 --- a/activesupport/lib/active_support/json/encoding.rb +++ b/activesupport/lib/active_support/json/encoding.rb @@ -1,3 +1,5 @@ +#encoding: us-ascii + require 'active_support/core_ext/object/to_json' require 'active_support/core_ext/module/delegation' require 'active_support/json/variable' @@ -98,13 +100,18 @@ module ActiveSupport "\010" => '\b', "\f" => '\f', "\n" => '\n', + "\xe2\x80\xa8" => '\u2028', + "\xe2\x80\xa9" => '\u2029', "\r" => '\r', "\t" => '\t', '"' => '\"', '\\' => '\\\\', '>' => '\u003E', '<' => '\u003C', - '&' => '\u0026' } + '&' => '\u0026', + "#{0xe2.chr}#{0x80.chr}#{0xa8.chr}" => '\u2028', + "#{0xe2.chr}#{0x80.chr}#{0xa9.chr}" => '\u2029', + } class << self # If true, use ISO 8601 format for dates and times. Otherwise, fall back @@ -121,9 +128,9 @@ module ActiveSupport def escape_html_entities_in_json=(value) self.escape_regex = \ if @escape_html_entities_in_json = value - /[\x00-\x1F"\\><&]/ + /\xe2\x80\xa8|\xe2\x80\xa9|[\x00-\x1F"\\><&]/ else - /[\x00-\x1F"\\]/ + /\xe2\x80\xa8|\xe2\x80\xa9|[\x00-\x1F"\\]/ end end diff --git a/activesupport/lib/active_support/key_generator.rb b/activesupport/lib/active_support/key_generator.rb index 37124fb7ae..598c46bce5 100644 --- a/activesupport/lib/active_support/key_generator.rb +++ b/activesupport/lib/active_support/key_generator.rb @@ -4,7 +4,7 @@ require 'openssl' module ActiveSupport # KeyGenerator is a simple wrapper around OpenSSL's implementation of PBKDF2 # It can be used to derive a number of keys for various purposes from a given secret. - # This lets rails applications have a single secure secret, but avoid reusing that + # This lets Rails applications have a single secure secret, but avoid reusing that # key in multiple incompatible contexts. class KeyGenerator def initialize(secret, options = {}) diff --git a/activesupport/lib/active_support/lazy_load_hooks.rb b/activesupport/lib/active_support/lazy_load_hooks.rb index e489512531..e2b8f0f648 100644 --- a/activesupport/lib/active_support/lazy_load_hooks.rb +++ b/activesupport/lib/active_support/lazy_load_hooks.rb @@ -1,5 +1,5 @@ module ActiveSupport - # lazy_load_hooks allows rails to lazily load a lot of components and thus + # lazy_load_hooks allows Rails to lazily load a lot of components and thus # making the app boot faster. Because of this feature now there is no need to # require <tt>ActiveRecord::Base</tt> at boot time purely to apply # configuration. Instead a hook is registered that applies configuration once diff --git a/activesupport/lib/active_support/multibyte/unicode.rb b/activesupport/lib/active_support/multibyte/unicode.rb index cbc1608349..f1dfff738c 100644 --- a/activesupport/lib/active_support/multibyte/unicode.rb +++ b/activesupport/lib/active_support/multibyte/unicode.rb @@ -218,51 +218,31 @@ module ActiveSupport # Passing +true+ will forcibly tidy all bytes, assuming that the string's # encoding is entirely CP1252 or ISO-8859-1. def tidy_bytes(string, force = false) + return string if string.empty? + if force - return string.unpack("C*").map do |b| - tidy_byte(b) - end.flatten.compact.pack("C*").unpack("U*").pack("U*") + return string.encode(Encoding::UTF_8, Encoding::Windows_1252, invalid: :replace, undef: :replace) end - bytes = string.unpack("C*") - conts_expected = 0 - last_lead = 0 - - bytes.each_index do |i| + # We can't transcode to the same format, so we choose a nearly-identical encoding. + # We're going to 'transcode' bytes from UTF-8 when possible, then fall back to + # CP1252 when we get errors. The final string will be 'converted' back to UTF-8 + # before returning. + reader = Encoding::Converter.new(Encoding::UTF_8, Encoding::UTF_8_MAC) - byte = bytes[i] - is_cont = byte > 127 && byte < 192 - is_lead = byte > 191 && byte < 245 - is_unused = byte > 240 - is_restricted = byte > 244 + source = string.dup + out = ''.force_encoding(Encoding::UTF_8_MAC) - # Impossible or highly unlikely byte? Clean it. - if is_unused || is_restricted - bytes[i] = tidy_byte(byte) - elsif is_cont - # Not expecting continuation byte? Clean up. Otherwise, now expect one less. - conts_expected == 0 ? bytes[i] = tidy_byte(byte) : conts_expected -= 1 - else - if conts_expected > 0 - # Expected continuation, but got ASCII or leading? Clean backwards up to - # the leading byte. - (1..(i - last_lead)).each {|j| bytes[i - j] = tidy_byte(bytes[i - j])} - conts_expected = 0 - end - if is_lead - # Final byte is leading? Clean it. - if i == bytes.length - 1 - bytes[i] = tidy_byte(bytes.last) - else - # Valid leading byte? Expect continuations determined by position of - # first zero bit, with max of 3. - conts_expected = byte < 224 ? 1 : byte < 240 ? 2 : 3 - last_lead = i - end - end - end + loop do + reader.primitive_convert(source, out) + _, _, _, error_bytes, _ = reader.primitive_errinfo + break if error_bytes.nil? + out << error_bytes.encode(Encoding::UTF_8_MAC, Encoding::Windows_1252, invalid: :replace, undef: :replace) end - bytes.empty? ? "" : bytes.flatten.compact.pack("C*").unpack("U*").pack("U*") + + reader.finish + + out.encode!(Encoding::UTF_8) end # Returns the KC normalization of the string by default. NFKC is diff --git a/activesupport/lib/active_support/ordered_options.rb b/activesupport/lib/active_support/ordered_options.rb index c9518bda79..e03bb4ca0f 100644 --- a/activesupport/lib/active_support/ordered_options.rb +++ b/activesupport/lib/active_support/ordered_options.rb @@ -40,6 +40,14 @@ module ActiveSupport end end + # +InheritableOptions+ provides a constructor to build an +OrderedOptions+ + # hash inherited from the another hash. + # + # Use this if you already have some hash and you want to create a new one based on it. + # + # h = ActiveSupport::InheritableOptions.new({ girl: 'Mary', boy: 'John' }) + # h.girl # => 'Mary' + # h.boy # => 'John' class InheritableOptions < OrderedOptions def initialize(parent = nil) if parent.kind_of?(OrderedOptions) diff --git a/activesupport/lib/active_support/test_case.rb b/activesupport/lib/active_support/test_case.rb index 8b392c36d0..f0962998a0 100644 --- a/activesupport/lib/active_support/test_case.rb +++ b/activesupport/lib/active_support/test_case.rb @@ -16,10 +16,28 @@ begin rescue LoadError end +module Minitest # :nodoc: + class << self + remove_method :__run + end + + def self.__run reporter, options # :nodoc: + # FIXME: MT5's runnables is not ordered. This is needed because + # we have have tests have cross-class order-dependent bugs. + suites = Runnable.runnables.sort_by { |ts| ts.name.to_s } + + parallel, serial = suites.partition { |s| s.test_order == :parallel } + + ParallelEach.new(parallel).map { |suite| suite.run reporter, options } + + serial.map { |suite| suite.run reporter, options } + end +end + module ActiveSupport - class TestCase < ::MiniTest::Unit::TestCase - Assertion = MiniTest::Assertion - alias_method :method_name, :__name__ + class TestCase < ::Minitest::Test + Assertion = Minitest::Assertion + + alias_method :method_name, :name $tags = {} def self.for_tag(tag) diff --git a/activesupport/lib/active_support/testing/autorun.rb b/activesupport/lib/active_support/testing/autorun.rb index c446adc16d..5aa5f46310 100644 --- a/activesupport/lib/active_support/testing/autorun.rb +++ b/activesupport/lib/active_support/testing/autorun.rb @@ -1,5 +1,5 @@ gem 'minitest' -require 'minitest/unit' +require 'minitest' -MiniTest::Unit.autorun +Minitest.autorun diff --git a/activesupport/lib/active_support/testing/isolation.rb b/activesupport/lib/active_support/testing/isolation.rb index d70d971538..9c52ae7768 100644 --- a/activesupport/lib/active_support/testing/isolation.rb +++ b/activesupport/lib/active_support/testing/isolation.rb @@ -40,6 +40,10 @@ module ActiveSupport def method_missing(name, *args) @calls << [name, args] end + + def info_signal + Signal.list['INFO'] + end end module Isolation @@ -68,16 +72,12 @@ module ActiveSupport end end - def run(runner) - _run_class_setup - - serialized = run_in_isolation do |isolated_runner| - super(isolated_runner) + def run + serialized = run_in_isolation do + super end - retval, proxy = Marshal.load(serialized) - proxy.__replay__(runner) - retval + Marshal.load(serialized) end module Forking @@ -86,9 +86,8 @@ module ActiveSupport pid = fork do read.close - proxy = ProxyTestResult.new - retval = yield proxy - write.puts [Marshal.dump([retval, proxy])].pack("m") + yield + write.puts [Marshal.dump(self.dup)].pack("m") exit! end diff --git a/activesupport/lib/active_support/testing/tagged_logging.rb b/activesupport/lib/active_support/testing/tagged_logging.rb index 9d43eb179f..f4cee64091 100644 --- a/activesupport/lib/active_support/testing/tagged_logging.rb +++ b/activesupport/lib/active_support/testing/tagged_logging.rb @@ -7,7 +7,7 @@ module ActiveSupport def before_setup if tagged_logger - heading = "#{self.class}: #{__name__}" + heading = "#{self.class}: #{name}" divider = '-' * heading.size tagged_logger.info divider tagged_logger.info heading |