diff options
Diffstat (limited to 'activesupport')
18 files changed, 199 insertions, 47 deletions
diff --git a/activesupport/CHANGELOG.md b/activesupport/CHANGELOG.md index df144dd00b..5fcea299f8 100644 --- a/activesupport/CHANGELOG.md +++ b/activesupport/CHANGELOG.md @@ -1,5 +1,9 @@ ## Rails 4.0.0 (unreleased) ## +* Inflections can now be defined per locale. `singularize` and `pluralize` accept locale as an extra argument. *David Celis* + +* `Object#try` will now return nil instead of raise a NoMethodError if the receiving object does not implement the method, but you can still get the old behavior by using the new `Object#try!` *DHH* + * `Time#change` now works with time values with offsets other than UTC or the local time zone. *Andrew White* * `ActiveSupport::Callbacks`: deprecate usage of filter object with `#before` and `#after` methods as `around` callback. *Bogdan Gusiev* diff --git a/activesupport/lib/active_support/cache/file_store.rb b/activesupport/lib/active_support/cache/file_store.rb index 5be63af342..2c1ad60d44 100644 --- a/activesupport/lib/active_support/cache/file_store.rb +++ b/activesupport/lib/active_support/cache/file_store.rb @@ -1,6 +1,5 @@ require 'active_support/core_ext/file/atomic' require 'active_support/core_ext/string/conversions' -require 'active_support/core_ext/object/inclusion' require 'uri/common' module ActiveSupport @@ -23,7 +22,7 @@ module ActiveSupport end def clear(options = nil) - root_dirs = Dir.entries(cache_path).reject{|f| f.in?(EXCLUDED_DIRS + [".gitkeep"])} + root_dirs = Dir.entries(cache_path).reject {|f| (EXCLUDED_DIRS + [".gitkeep"]).include?(f)} FileUtils.rm_r(root_dirs.collect{|f| File.join(cache_path, f)}) end @@ -151,7 +150,7 @@ module ActiveSupport # Delete empty directories in the cache. def delete_empty_directories(dir) return if dir == cache_path - if Dir.entries(dir).reject{|f| f.in?(EXCLUDED_DIRS)}.empty? + if Dir.entries(dir).reject {|f| EXCLUDED_DIRS.include?(f)}.empty? File.delete(dir) rescue nil delete_empty_directories(File.dirname(dir)) end @@ -165,7 +164,7 @@ module ActiveSupport def search_dir(dir, &callback) return if !File.exist?(dir) Dir.foreach(dir) do |d| - next if d.in?(EXCLUDED_DIRS) + next if EXCLUDED_DIRS.include?(d) name = File.join(dir, d) if File.directory?(name) search_dir(name, &callback) diff --git a/activesupport/lib/active_support/callbacks.rb b/activesupport/lib/active_support/callbacks.rb index 6cc875c69a..3f7d0e401a 100644 --- a/activesupport/lib/active_support/callbacks.rb +++ b/activesupport/lib/active_support/callbacks.rb @@ -3,7 +3,6 @@ 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 'active_support/core_ext/object/inclusion' module ActiveSupport # \Callbacks are code hooks that are run at key points in an object's lifecycle. @@ -78,7 +77,7 @@ module ActiveSupport private # A hook invoked everytime a before callback is halted. - # This can be overriden in AS::Callback implementors in order + # This can be overridden in AS::Callback implementors in order # to provide better debugging/logging. def halted_callback_hook(filter) end @@ -353,7 +352,7 @@ module ActiveSupport # CallbackChain. # def __update_callbacks(name, filters = [], block = nil) #:nodoc: - type = filters.first.in?([:before, :after, :around]) ? filters.shift : :before + type = [:before, :after, :around].include?(filters.first) ? filters.shift : :before options = filters.last.is_a?(Hash) ? filters.pop : {} filters.unshift(block) if block diff --git a/activesupport/lib/active_support/concurrency/latch.rb b/activesupport/lib/active_support/concurrency/latch.rb new file mode 100644 index 0000000000..1507de433e --- /dev/null +++ b/activesupport/lib/active_support/concurrency/latch.rb @@ -0,0 +1,27 @@ +require 'thread' +require 'monitor' + +module ActiveSupport + module Concurrency + class Latch + def initialize(count = 1) + @count = count + @lock = Monitor.new + @cv = @lock.new_cond + end + + def release + @lock.synchronize do + @count -= 1 if @count > 0 + @cv.broadcast if @count.zero? + end + end + + def await + @lock.synchronize do + @cv.wait_while { @count > 0 } + end + end + end + 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 e5f81078ee..0d5f3501e5 100644 --- a/activesupport/lib/active_support/core_ext/object/to_param.rb +++ b/activesupport/lib/active_support/core_ext/object/to_param.rb @@ -6,18 +6,21 @@ class Object end class NilClass + # Returns +self+. def to_param self end end class TrueClass + # Returns +self+. def to_param self end end class FalseClass + # Returns +self+. def to_param self end @@ -35,12 +38,12 @@ class Hash # Returns a string representation of the receiver suitable for use as a URL # query string: # - # {:name => 'David', :nationality => 'Danish'}.to_param + # {name: 'David', nationality: 'Danish'}.to_param # # => "name=David&nationality=Danish" # # An optional namespace can be passed to enclose the param names: # - # {:name => 'David', :nationality => 'Danish'}.to_param('user') + # {name: 'David', nationality: 'Danish'}.to_param('user') # # => "user[name]=David&user[nationality]=Danish" # # The string pairs "key=value" that conform the query string diff --git a/activesupport/lib/active_support/core_ext/object/try.rb b/activesupport/lib/active_support/core_ext/object/try.rb index 16a799ec03..9974b61078 100644 --- a/activesupport/lib/active_support/core_ext/object/try.rb +++ b/activesupport/lib/active_support/core_ext/object/try.rb @@ -5,6 +5,9 @@ class Object # *Unlike* that method however, a +NoMethodError+ exception will *not* be raised # and +nil+ will be returned instead, if the receiving object is a +nil+ object or NilClass. # + # This is also true if the receiving object does not implemented the tried method. It will + # return +nil+ in that case as well. + # # If try is called without a method to call, it will yield any given block with the object. # # Please also note that +try+ is defined on +Object+, therefore it won't work with @@ -31,6 +34,16 @@ class Object if a.empty? && block_given? yield self else + public_send(*a, &b) if respond_to?(a.first) + end + end + + # Same as #try, but will raise a NoMethodError exception if the receiving is not nil and + # does not implemented the tried method. + def try!(*a, &b) + if a.empty? && block_given? + yield self + else public_send(*a, &b) end end @@ -50,4 +63,8 @@ class NilClass def try(*args) nil end + + def try!(*args) + nil + end end diff --git a/activesupport/lib/active_support/core_ext/string/inflections.rb b/activesupport/lib/active_support/core_ext/string/inflections.rb index efa2d43f20..6edfcd7493 100644 --- a/activesupport/lib/active_support/core_ext/string/inflections.rb +++ b/activesupport/lib/active_support/core_ext/string/inflections.rb @@ -13,6 +13,11 @@ class String # the singular form will be returned if <tt>count == 1</tt>. # For any other value of +count+ the plural will be returned. # + # If the optional parameter +locale+ is specified, + # the word will be pluralized as a word of that language. + # By default, this parameter is set to <tt>:en</tt>. + # You must define your own inflection rules for languages other than English. + # # 'post'.pluralize # => "posts" # 'octopus'.pluralize # => "octopi" # 'sheep'.pluralize # => "sheep" @@ -21,15 +26,23 @@ class String # 'CamelOctopus'.pluralize # => "CamelOctopi" # 'apple'.pluralize(1) # => "apple" # 'apple'.pluralize(2) # => "apples" - def pluralize(count = nil) + # 'ley'.pluralize(:es) # => "leyes" + # 'ley'.pluralize(1, :es) # => "ley" + def pluralize(count = nil, locale = :en) + locale = count if count.is_a?(Symbol) if count == 1 self else - ActiveSupport::Inflector.pluralize(self) + ActiveSupport::Inflector.pluralize(self, locale) end end # The reverse of +pluralize+, returns the singular form of a word in a string. + # + # If the optional parameter +locale+ is specified, + # the word will be singularized as a word of that language. + # By default, this paramter is set to <tt>:en</tt>. + # You must define your own inflection rules for languages other than English. # # 'posts'.singularize # => "post" # 'octopi'.singularize # => "octopus" @@ -37,8 +50,9 @@ class String # 'word'.singularize # => "word" # 'the blue mailmen'.singularize # => "the blue mailman" # 'CamelOctopi'.singularize # => "CamelOctopus" - def singularize - ActiveSupport::Inflector.singularize(self) + # 'leyes'.singularize(:es) # => "ley" + def singularize(locale = :en) + ActiveSupport::Inflector.singularize(self, locale) end # +constantize+ tries to find a declared constant with the name specified 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 5226ff0cbe..c17d695967 100644 --- a/activesupport/lib/active_support/core_ext/string/output_safety.rb +++ b/activesupport/lib/active_support/core_ext/string/output_safety.rb @@ -3,9 +3,9 @@ require 'active_support/core_ext/kernel/singleton_class' class ERB module Util - HTML_ESCAPE = { '&' => '&', '>' => '>', '<' => '<', '"' => '"' } + HTML_ESCAPE = { '&' => '&', '>' => '>', '<' => '<', '"' => '"', "'" => ''' } JSON_ESCAPE = { '&' => '\u0026', '>' => '\u003E', '<' => '\u003C' } - HTML_ESCAPE_ONCE_REGEXP = /[\"><]|&(?!([a-zA-Z]+|(#\d+));)/ + HTML_ESCAPE_ONCE_REGEXP = /["><']|&(?!([a-zA-Z]+|(#\d+));)/ JSON_ESCAPE_REGEXP = /[&"><]/ # A utility method for escaping HTML tag characters. @@ -21,7 +21,7 @@ class ERB if s.html_safe? s else - s.encode(s.encoding, :xml => :attr)[1...-1].html_safe + s.gsub(/[&"'><]/, HTML_ESCAPE).html_safe end end diff --git a/activesupport/lib/active_support/inflections.rb b/activesupport/lib/active_support/inflections.rb index ca2d8cb270..ef882ebd09 100644 --- a/activesupport/lib/active_support/inflections.rb +++ b/activesupport/lib/active_support/inflections.rb @@ -1,7 +1,7 @@ require 'active_support/inflector/inflections' module ActiveSupport - Inflector.inflections do |inflect| + Inflector.inflections(:en) do |inflect| inflect.plural(/$/, 's') inflect.plural(/s$/i, 's') inflect.plural(/^(ax|test)is$/i, '\1es') diff --git a/activesupport/lib/active_support/inflector/inflections.rb b/activesupport/lib/active_support/inflector/inflections.rb index c9e50a9462..091692e5a4 100644 --- a/activesupport/lib/active_support/inflector/inflections.rb +++ b/activesupport/lib/active_support/inflector/inflections.rb @@ -1,13 +1,15 @@ require 'active_support/core_ext/array/prepend_and_append' +require 'active_support/i18n' module ActiveSupport module Inflector extend self # A singleton instance of this class is yielded by Inflector.inflections, which can then be used to specify additional - # inflection rules. + # inflection rules. If passed an optional locale, rules for other languages can be specified. The default locale is + # <tt>:en</tt>. Only rules for English are provided. # - # ActiveSupport::Inflector.inflections do |inflect| + # ActiveSupport::Inflector.inflections(:en) do |inflect| # inflect.plural /^(ox)$/i, '\1\2en' # inflect.singular /^(ox)en/i, '\1' # @@ -20,8 +22,9 @@ module ActiveSupport # pluralization and singularization rules that is runs. This guarantees that your rules run before any of the rules that may # already have been loaded. class Inflections - def self.instance - @__instance__ ||= new + def self.instance(locale = :en) + @__instance__ ||= Hash.new { |h, k| h[k] = new } + @__instance__[locale] end attr_reader :plurals, :singulars, :uncountables, :humans, :acronyms, :acronym_regex @@ -160,16 +163,18 @@ module ActiveSupport end # Yields a singleton instance of Inflector::Inflections so you can specify additional - # inflector rules. + # inflector rules. If passed an optional locale, rules for other languages can be specified. + # If not specified, defaults to <tt>:en</tt>. Only rules for English are provided. + # # - # ActiveSupport::Inflector.inflections do |inflect| + # ActiveSupport::Inflector.inflections(:en) do |inflect| # inflect.uncountable "rails" # end - def inflections + def inflections(locale = :en) if block_given? - yield Inflections.instance + yield Inflections.instance(locale) else - Inflections.instance + Inflections.instance(locale) end end end diff --git a/activesupport/lib/active_support/inflector/methods.rb b/activesupport/lib/active_support/inflector/methods.rb index c14a43de0d..44214d16fa 100644 --- a/activesupport/lib/active_support/inflector/methods.rb +++ b/activesupport/lib/active_support/inflector/methods.rb @@ -10,31 +10,41 @@ module ActiveSupport # # The Rails core team has stated patches for the inflections library will not be accepted # in order to avoid breaking legacy applications which may be relying on errant inflections. - # If you discover an incorrect inflection and require it for your application, you'll need - # to correct it yourself (explained below). + # If you discover an incorrect inflection and require it for your application or wish to + # define rules for languages other than English, please correct or add them yourself (explained below). module Inflector extend self # Returns the plural form of the word in the string. # + # If passed an optional +locale+ parameter, the word will be + # pluralized using rules defined for that language. By default, + # this parameter is set to <tt>:en</tt>. + # # "post".pluralize # => "posts" # "octopus".pluralize # => "octopi" # "sheep".pluralize # => "sheep" # "words".pluralize # => "words" # "CamelOctopus".pluralize # => "CamelOctopi" - def pluralize(word) - apply_inflections(word, inflections.plurals) + # "ley".pluralize(:es) # => "leyes" + def pluralize(word, locale = :en) + apply_inflections(word, inflections(locale).plurals) end # The reverse of +pluralize+, returns the singular form of a word in a string. # + # If passed an optional +locale+ parameter, the word will be + # pluralized using rules defined for that language. By default, + # this parameter is set to <tt>:en</tt>. + # # "posts".singularize # => "post" # "octopi".singularize # => "octopus" # "sheep".singularize # => "sheep" # "word".singularize # => "word" # "CamelOctopi".singularize # => "CamelOctopus" - def singularize(word) - apply_inflections(word, inflections.singulars) + # "leyes".singularize(:es) # => "ley" + def singularize(word, locale = :en) + apply_inflections(word, inflections(locale).singulars) end # By default, +camelize+ converts strings to UpperCamelCase. If the argument to +camelize+ diff --git a/activesupport/lib/active_support/rails.rb b/activesupport/lib/active_support/rails.rb new file mode 100644 index 0000000000..b05c3ff126 --- /dev/null +++ b/activesupport/lib/active_support/rails.rb @@ -0,0 +1,27 @@ +# This is private interface. +# +# Rails components cherry pick from Active Support as needed, but there are a +# few features that are used for sure some way or another and it is not worth +# to put individual requires absolutely everywhere. Think blank? for example. +# +# This file is loaded by every Rails component except Active Support itself, +# but it does not belong to the Rails public interface. It is internal to +# Rails and can change anytime. + +# Defines Object#blank? and Object#present?. +require 'active_support/core_ext/object/blank' + +# Rails own autoload, eager_load, etc. +require 'active_support/dependencies/autoload' + +# Support for ClassMethods and the included macro. +require 'active_support/concern' + +# Defines Class#class_attribute. +require 'active_support/core_ext/class/attribute' + +# Defines Module#delegate. +require 'active_support/core_ext/module/delegation' + +# Defines ActiveSupport::Deprecation. +require 'active_support/deprecation' diff --git a/activesupport/lib/active_support/time_with_zone.rb b/activesupport/lib/active_support/time_with_zone.rb index 451520ac5c..93c2d614f5 100644 --- a/activesupport/lib/active_support/time_with_zone.rb +++ b/activesupport/lib/active_support/time_with_zone.rb @@ -1,6 +1,5 @@ require 'active_support/values/time_zone' require 'active_support/core_ext/object/acts_like' -require 'active_support/core_ext/object/inclusion' module ActiveSupport # A Time-like class that can represent a time in any time zone. Necessary because standard Ruby Time instances are @@ -339,7 +338,7 @@ module ActiveSupport end def duration_of_variable_length?(obj) - ActiveSupport::Duration === obj && obj.parts.any? {|p| p[0].in?([:years, :months, :days]) } + ActiveSupport::Duration === obj && obj.parts.any? {|p| [:years, :months, :days].include?(p[0]) } end def wrap_with_time_zone(time) diff --git a/activesupport/test/core_ext/object_and_class_ext_test.rb b/activesupport/test/core_ext/object_and_class_ext_test.rb index 98ab82609e..ec7dd6d4fb 100644 --- a/activesupport/test/core_ext/object_and_class_ext_test.rb +++ b/activesupport/test/core_ext/object_and_class_ext_test.rb @@ -99,13 +99,25 @@ class ObjectTryTest < ActiveSupport::TestCase def test_nonexisting_method method = :undefined_method assert !@string.respond_to?(method) - assert_raise(NoMethodError) { @string.try(method) } + assert_nil @string.try(method) end def test_nonexisting_method_with_arguments method = :undefined_method assert !@string.respond_to?(method) - assert_raise(NoMethodError) { @string.try(method, 'llo', 'y') } + assert_nil @string.try(method, 'llo', 'y') + end + + def test_nonexisting_method_bang + method = :undefined_method + assert !@string.respond_to?(method) + assert_raise(NoMethodError) { @string.try!(method) } + end + + def test_nonexisting_method_with_arguments_bang + method = :undefined_method + assert !@string.respond_to?(method) + assert_raise(NoMethodError) { @string.try!(method, 'llo', 'y') } end def test_valid_method @@ -139,6 +151,18 @@ class ObjectTryTest < ActiveSupport::TestCase assert_equal false, ran end + def test_try_with_private_method_bang + klass = Class.new do + private + + def private_method + 'private method' + end + end + + assert_raise(NoMethodError) { klass.new.try!(:private_method) } + end + def test_try_with_private_method klass = Class.new do private @@ -148,6 +172,6 @@ class ObjectTryTest < ActiveSupport::TestCase end end - assert_raise(NoMethodError) { klass.new.try(:private_method) } + assert_nil klass.new.try(:private_method) end end diff --git a/activesupport/test/core_ext/string_ext_test.rb b/activesupport/test/core_ext/string_ext_test.rb index e5b774425e..3b08ebe35f 100644 --- a/activesupport/test/core_ext/string_ext_test.rb +++ b/activesupport/test/core_ext/string_ext_test.rb @@ -498,8 +498,8 @@ class OutputSafetyTest < ActiveSupport::TestCase end test "ERB::Util.html_escape should escape unsafe characters" do - string = '<>&"' - expected = '<>&"' + string = '<>&"\'' + expected = '<>&"'' assert_equal expected, ERB::Util.html_escape(string) end diff --git a/activesupport/test/inflector_test.rb b/activesupport/test/inflector_test.rb index 1f32e4ff92..cd91002147 100644 --- a/activesupport/test/inflector_test.rb +++ b/activesupport/test/inflector_test.rb @@ -354,6 +354,35 @@ class InflectorTest < ActiveSupport::TestCase RUBY end + def test_inflector_locality + ActiveSupport::Inflector.inflections(:es) do |inflect| + inflect.plural(/$/, 's') + inflect.plural(/z$/i, 'ces') + + inflect.singular(/s$/, '') + inflect.singular(/es$/, '') + + inflect.irregular('el', 'los') + end + + assert_equal('hijos', 'hijo'.pluralize(:es)) + assert_equal('luces', 'luz'.pluralize(:es)) + assert_equal('luzs', 'luz'.pluralize) + + assert_equal('sociedad', 'sociedades'.singularize(:es)) + assert_equal('sociedade', 'sociedades'.singularize) + + assert_equal('los', 'el'.pluralize(:es)) + assert_equal('els', 'el'.pluralize) + + ActiveSupport::Inflector.inflections(:es) { |inflect| inflect.clear } + + assert ActiveSupport::Inflector.inflections(:es).plurals.empty? + assert ActiveSupport::Inflector.inflections(:es).singulars.empty? + assert !ActiveSupport::Inflector.inflections.plurals.empty? + assert !ActiveSupport::Inflector.inflections.singulars.empty? + end + def test_clear_all with_dup do ActiveSupport::Inflector.inflections do |inflect| @@ -467,7 +496,7 @@ class InflectorTest < ActiveSupport::TestCase # there are module functions that access ActiveSupport::Inflector.inflections, # so we need to replace the singleton itself. def with_dup - original = ActiveSupport::Inflector.inflections + original = ActiveSupport::Inflector::Inflections.instance_variable_get(:@__instance__) ActiveSupport::Inflector::Inflections.instance_variable_set(:@__instance__, original.dup) ensure ActiveSupport::Inflector::Inflections.instance_variable_set(:@__instance__, original) diff --git a/activesupport/test/ordered_hash_test.rb b/activesupport/test/ordered_hash_test.rb index e8defd396b..ac85ba1f21 100644 --- a/activesupport/test/ordered_hash_test.rb +++ b/activesupport/test/ordered_hash_test.rb @@ -226,12 +226,8 @@ class OrderedHashTest < ActiveSupport::TestCase end def test_alternate_initialization_raises_exception_on_odd_length_args - begin + assert_raises ArgumentError do ActiveSupport::OrderedHash[1,2,3,4,5] - flunk "Hash::[] should have raised an exception on initialization " + - "with an odd number of parameters" - rescue ArgumentError => e - assert_equal "odd number of arguments for Hash", e.message end end diff --git a/activesupport/test/transliterate_test.rb b/activesupport/test/transliterate_test.rb index b7076e9e58..b5d8142458 100644 --- a/activesupport/test/transliterate_test.rb +++ b/activesupport/test/transliterate_test.rb @@ -1,7 +1,6 @@ # encoding: utf-8 require 'abstract_unit' require 'active_support/inflector/transliterate' -require 'active_support/core_ext/object/inclusion' class TransliterateTest < ActiveSupport::TestCase @@ -16,7 +15,7 @@ class TransliterateTest < ActiveSupport::TestCase # create string with range of Unicode"s western characters with # diacritics, excluding the division and multiplication signs which for # some reason or other are floating in the middle of all the letters. - string = (0xC0..0x17E).to_a.reject {|c| c.in?([0xD7, 0xF7])}.pack("U*") + string = (0xC0..0x17E).to_a.reject {|c| [0xD7, 0xF7].include?(c)}.pack("U*") string.each_char do |char| assert_match %r{^[a-zA-Z']*$}, ActiveSupport::Inflector.transliterate(string) end |