activesupport 3.0.0.beta3 → 3.0.0.beta4

Sign up to get free protection for your applications and to get access to all the features.

Potentially problematic release.


This version of activesupport might be problematic. Click here for more details.

Files changed (63) hide show
  1. data/CHANGELOG +57 -0
  2. data/lib/active_support/builder.rb +6 -0
  3. data/lib/active_support/cache.rb +428 -70
  4. data/lib/active_support/cache/compressed_mem_cache_store.rb +6 -15
  5. data/lib/active_support/cache/file_store.rb +139 -41
  6. data/lib/active_support/cache/mem_cache_store.rb +115 -76
  7. data/lib/active_support/cache/memory_store.rb +127 -27
  8. data/lib/active_support/cache/strategy/local_cache.rb +109 -57
  9. data/lib/active_support/cache/synchronized_memory_store.rb +2 -38
  10. data/lib/active_support/callbacks.rb +27 -27
  11. data/lib/active_support/configurable.rb +19 -18
  12. data/lib/active_support/core_ext/array/conversions.rb +30 -26
  13. data/lib/active_support/core_ext/array/random_access.rb +19 -5
  14. data/lib/active_support/core_ext/benchmark.rb +0 -12
  15. data/lib/active_support/core_ext/class/attribute.rb +1 -4
  16. data/lib/active_support/core_ext/class/inheritable_attributes.rb +3 -0
  17. data/lib/active_support/core_ext/date/calculations.rb +27 -8
  18. data/lib/active_support/core_ext/date/conversions.rb +1 -0
  19. data/lib/active_support/core_ext/date_time/conversions.rb +9 -3
  20. data/lib/active_support/core_ext/file.rb +1 -0
  21. data/lib/active_support/core_ext/hash/conversions.rb +14 -137
  22. data/lib/active_support/core_ext/kernel/debugger.rb +1 -1
  23. data/lib/active_support/core_ext/kernel/reporting.rb +2 -1
  24. data/lib/active_support/core_ext/load_error.rb +1 -0
  25. data/lib/active_support/core_ext/logger.rb +1 -1
  26. data/lib/active_support/core_ext/module/attr_internal.rb +2 -2
  27. data/lib/active_support/core_ext/object/to_param.rb +2 -2
  28. data/lib/active_support/core_ext/object/with_options.rb +2 -0
  29. data/lib/active_support/core_ext/string.rb +1 -0
  30. data/lib/active_support/core_ext/string/conversions.rb +35 -1
  31. data/lib/active_support/core_ext/string/encoding.rb +11 -0
  32. data/lib/active_support/core_ext/string/filters.rb +29 -0
  33. data/lib/active_support/core_ext/string/inflections.rb +0 -11
  34. data/lib/active_support/core_ext/string/interpolation.rb +1 -0
  35. data/lib/active_support/core_ext/string/multibyte.rb +16 -19
  36. data/lib/active_support/core_ext/time/calculations.rb +7 -6
  37. data/lib/active_support/core_ext/uri.rb +8 -3
  38. data/lib/active_support/dependencies.rb +33 -1
  39. data/lib/active_support/duration.rb +1 -0
  40. data/lib/active_support/hash_with_indifferent_access.rb +5 -1
  41. data/lib/active_support/i18n.rb +7 -2
  42. data/lib/active_support/inflector/transliterate.rb +58 -38
  43. data/lib/active_support/json/encoding.rb +28 -5
  44. data/lib/active_support/lazy_load_hooks.rb +14 -4
  45. data/lib/active_support/locale/en.yml +4 -1
  46. data/lib/active_support/message_verifier.rb +4 -4
  47. data/lib/active_support/multibyte.rb +1 -19
  48. data/lib/active_support/multibyte/chars.rb +143 -427
  49. data/lib/active_support/multibyte/unicode.rb +393 -0
  50. data/lib/active_support/notifications/fanout.rb +15 -5
  51. data/lib/active_support/notifications/instrumenter.rb +10 -4
  52. data/lib/active_support/railtie.rb +36 -0
  53. data/lib/active_support/rescuable.rb +1 -0
  54. data/lib/active_support/ruby/shim.rb +1 -0
  55. data/lib/active_support/testing/declarative.rb +1 -1
  56. data/lib/active_support/testing/isolation.rb +2 -1
  57. data/lib/active_support/testing/setup_and_teardown.rb +3 -0
  58. data/lib/active_support/values/time_zone.rb +20 -30
  59. data/lib/active_support/values/unicode_tables.dat +0 -0
  60. data/lib/active_support/version.rb +1 -1
  61. data/lib/active_support/xml_mini.rb +126 -1
  62. metadata +8 -61
  63. data/lib/active_support/multibyte/unicode_database.rb +0 -71
@@ -2,7 +2,7 @@ module Kernel
2
2
  unless respond_to?(:debugger)
3
3
  # Starts a debugging session if ruby-debug has been loaded (call rails server --debugger to do load it).
4
4
  def debugger
5
- message = "\n***** Debugger requested, but was not available: Start server with --debugger to enable *****\n"
5
+ message = "\n***** Debugger requested, but was not available (ensure ruby-debug is listed in Gemfile/installed as gem): Start server with --debugger to enable *****\n"
6
6
  defined?(Rails) ? Rails.logger.info(message) : $stderr.puts(message)
7
7
  end
8
8
  end
@@ -1,3 +1,4 @@
1
+ require 'rbconfig'
1
2
  module Kernel
2
3
  # Sets $VERBOSE to nil for the duration of the block and back to its original value afterwards.
3
4
  #
@@ -37,7 +38,7 @@ module Kernel
37
38
  # puts 'But this will'
38
39
  def silence_stream(stream)
39
40
  old_stream = stream.dup
40
- stream.reopen(RUBY_PLATFORM =~ /mswin|mingw/ ? 'NUL:' : '/dev/null')
41
+ stream.reopen(Config::CONFIG['host_os'] =~ /mswin|mingw/ ? 'NUL:' : '/dev/null')
41
42
  stream.sync = true
42
43
  yield
43
44
  ensure
@@ -3,6 +3,7 @@ class LoadError
3
3
  /^no such file to load -- (.+)$/i,
4
4
  /^Missing \w+ (?:file\s*)?([^\s]+.rb)$/i,
5
5
  /^Missing API definition file in (.+)$/i,
6
+ /^cannot load such file -- (.+)$/i,
6
7
  ]
7
8
 
8
9
  def path
@@ -3,7 +3,7 @@ require 'active_support/core_ext/class/attribute_accessors'
3
3
  # Adds the 'around_level' method to Logger.
4
4
  class Logger #:nodoc:
5
5
  def self.define_around_helper(level)
6
- module_eval <<-end_eval
6
+ module_eval <<-end_eval, __FILE__, __LINE__ + 1
7
7
  def around_#{level}(before_message, after_message, &block) # def around_debug(before_message, after_message, &block)
8
8
  self.#{level}(before_message) # self.debug(before_message)
9
9
  return_value = block.call(self) # return_value = block.call(self)
@@ -2,14 +2,14 @@ class Module
2
2
  # Declares an attribute reader backed by an internally-named instance variable.
3
3
  def attr_internal_reader(*attrs)
4
4
  attrs.each do |attr|
5
- module_eval "def #{attr}() #{attr_internal_ivar_name(attr)} end"
5
+ module_eval "def #{attr}() #{attr_internal_ivar_name(attr)} end", __FILE__, __LINE__
6
6
  end
7
7
  end
8
8
 
9
9
  # Declares an attribute writer backed by an internally-named instance variable.
10
10
  def attr_internal_writer(*attrs)
11
11
  attrs.each do |attr|
12
- module_eval "def #{attr}=(v) #{attr_internal_ivar_name(attr)} = v end"
12
+ module_eval "def #{attr}=(v) #{attr_internal_ivar_name(attr)} = v end", __FILE__, __LINE__
13
13
  end
14
14
  end
15
15
 
@@ -38,9 +38,9 @@ class Hash
38
38
  # passed to enclose the param names (see example below).
39
39
  #
40
40
  # ==== Examples
41
- # { :name => 'David', :nationality => 'Danish' }.to_query # => "name=David&nationality=Danish"
41
+ # { :name => 'David', :nationality => 'Danish' }.to_param # => "name=David&nationality=Danish"
42
42
  #
43
- # { :name => 'David', :nationality => 'Danish' }.to_query('user') # => "user[name]=David&user[nationality]=Danish"
43
+ # { :name => 'David', :nationality => 'Danish' }.to_param('user') # => "user[name]=David&user[nationality]=Danish"
44
44
  def to_param(namespace = nil)
45
45
  collect do |key, value|
46
46
  value.to_query(namespace ? "#{namespace}[#{key}]" : key)
@@ -1,3 +1,5 @@
1
+ require 'active_support/option_merger'
2
+
1
3
  class Object
2
4
  # An elegant way to factor duplication out of options passed to a series of
3
5
  # method calls. Each method called in the block, with the block variable as
@@ -9,3 +9,4 @@ require 'active_support/core_ext/string/behavior'
9
9
  require 'active_support/core_ext/string/interpolation'
10
10
  require 'active_support/core_ext/string/output_safety'
11
11
  require 'active_support/core_ext/string/exclude'
12
+ require 'active_support/core_ext/string/encoding'
@@ -3,25 +3,59 @@ require 'active_support/core_ext/time/publicize_conversion_methods'
3
3
  require 'active_support/core_ext/time/calculations'
4
4
 
5
5
  class String
6
- # 'a'.ord == 'a'[0] for Ruby 1.9 forward compatibility.
6
+ # Returns the codepoint of the first character of the string, assuming a
7
+ # single-byte character encoding:
8
+ #
9
+ # "a".ord # => 97
10
+ # "à".ord # => 224, in ISO-8859-1
11
+ #
12
+ # This method is defined in Ruby 1.8 for Ruby 1.9 forward compatibility on
13
+ # these character encodings.
14
+ #
15
+ # <tt>ActiveSupport::Multibyte::Chars#ord</tt> is forward compatible with
16
+ # Ruby 1.9 on UTF8 strings:
17
+ #
18
+ # "a".mb_chars.ord # => 97
19
+ # "à".mb_chars.ord # => 224, in UTF8
20
+ #
21
+ # Note that the 224 is different in both examples. In ISO-8859-1 "à" is
22
+ # represented as a single byte, 224. In UTF8 it is represented with two
23
+ # bytes, namely 195 and 160, but its Unicode codepoint is 224. If we
24
+ # call +ord+ on the UTF8 string "à" the return value will be 195. That is
25
+ # not an error, because UTF8 is unsupported, the call itself would be
26
+ # bogus.
7
27
  def ord
8
28
  self[0]
9
29
  end unless method_defined?(:ord)
10
30
 
11
31
  # Form can be either :utc (default) or :local.
12
32
  def to_time(form = :utc)
33
+ return nil if self.blank?
13
34
  d = ::Date._parse(self, false).values_at(:year, :mon, :mday, :hour, :min, :sec, :sec_fraction).map { |arg| arg || 0 }
14
35
  d[6] *= 1000000
15
36
  ::Time.send("#{form}_time", *d)
16
37
  end
17
38
 
18
39
  def to_date
40
+ return nil if self.blank?
19
41
  ::Date.new(*::Date._parse(self, false).values_at(:year, :mon, :mday))
20
42
  end
21
43
 
22
44
  def to_datetime
45
+ return nil if self.blank?
23
46
  d = ::Date._parse(self, false).values_at(:year, :mon, :mday, :hour, :min, :sec, :zone, :sec_fraction).map { |arg| arg || 0 }
24
47
  d[5] += d.pop
25
48
  ::DateTime.civil(*d)
26
49
  end
50
+
51
+ # +constantize+ tries to find a declared constant with the name specified
52
+ # in the string. It raises a NameError when the name is not in CamelCase
53
+ # or is not initialized.
54
+ #
55
+ # Examples
56
+ # "Module".constantize # => Module
57
+ # "Class".constantize # => Class
58
+ def constantize
59
+ ActiveSupport::Inflector.constantize(self)
60
+ end
27
61
  end
@@ -0,0 +1,11 @@
1
+ class String
2
+ if defined?(Encoding) && "".respond_to?(:encode)
3
+ def encoding_aware?
4
+ true
5
+ end
6
+ else
7
+ def encoding_aware?
8
+ false
9
+ end
10
+ end
11
+ end
@@ -1,3 +1,5 @@
1
+ require 'active_support/core_ext/string/multibyte'
2
+
1
3
  class String
2
4
  # Returns the string, first removing all whitespace on both ends of
3
5
  # the string, and then changing remaining consecutive whitespace
@@ -17,4 +19,31 @@ class String
17
19
  gsub!(/\s+/, ' ')
18
20
  self
19
21
  end
22
+
23
+ # Truncates a given +text+ after a given <tt>length</tt> if +text+ is longer than <tt>length</tt>:
24
+ #
25
+ # "Once upon a time in a world far far away".truncate(27)
26
+ # # => "Once upon a time in a wo..."
27
+ #
28
+ # The last characters will be replaced with the <tt>:omission</tt> string (defaults to "...")
29
+ # for a total length not exceeding <tt>:length</tt>:
30
+ #
31
+ # "Once upon a time in a world far far away".truncate(27, :separator => ' ')
32
+ # # => "Once upon a time in a..."
33
+ #
34
+ # Pass a <tt>:separator</tt> to truncate +text+ at a natural break:
35
+ #
36
+ # "And they found that many people were sleeping better.".truncate(25, :omission => "... (continued)")
37
+ # # => "And they f... (continued)"
38
+ def truncate(length, options = {})
39
+ text = self.dup
40
+ options[:omission] ||= "..."
41
+
42
+ length_with_room_for_omission = length - options[:omission].mb_chars.length
43
+ chars = text.mb_chars
44
+ stop = options[:separator] ?
45
+ (chars.rindex(options[:separator].mb_chars, length_with_room_for_omission) || length_with_room_for_omission) : length_with_room_for_omission
46
+
47
+ (chars.length > length ? chars[0...stop] + options[:omission] : text).to_s
48
+ end
20
49
  end
@@ -146,15 +146,4 @@ class String
146
146
  def foreign_key(separate_class_name_and_id_with_underscore = true)
147
147
  ActiveSupport::Inflector.foreign_key(self, separate_class_name_and_id_with_underscore)
148
148
  end
149
-
150
- # +constantize+ tries to find a declared constant with the name specified
151
- # in the string. It raises a NameError when the name is not in CamelCase
152
- # or is not initialized.
153
- #
154
- # Examples
155
- # "Module".constantize # => Module
156
- # "Class".constantize # => Class
157
- def constantize
158
- ActiveSupport::Inflector.constantize(self)
159
- end
160
149
  end
@@ -1 +1,2 @@
1
+ require 'active_support/i18n'
1
2
  require 'i18n/core_ext/string/interpolate'
@@ -2,7 +2,7 @@
2
2
  require 'active_support/multibyte'
3
3
 
4
4
  class String
5
- unless '1.9'.respond_to?(:force_encoding)
5
+ if '1.9'.respond_to?(:force_encoding)
6
6
  # == Multibyte proxy
7
7
  #
8
8
  # +mb_chars+ is a multibyte safe proxy for string methods.
@@ -37,30 +37,13 @@ class String
37
37
  # For more information about the methods defined on the Chars proxy see ActiveSupport::Multibyte::Chars. For
38
38
  # information about how to change the default Multibyte behaviour see ActiveSupport::Multibyte.
39
39
  def mb_chars
40
- if ActiveSupport::Multibyte.proxy_class.wants?(self)
40
+ if ActiveSupport::Multibyte.proxy_class.consumes?(self)
41
41
  ActiveSupport::Multibyte.proxy_class.new(self)
42
42
  else
43
43
  self
44
44
  end
45
45
  end
46
-
47
- # Returns true if the string has UTF-8 semantics (a String used for purely byte resources is unlikely to have
48
- # them), returns false otherwise.
49
- def is_utf8?
50
- ActiveSupport::Multibyte::Chars.consumes?(self)
51
- end
52
46
 
53
- unless '1.8.7 and later'.respond_to?(:chars)
54
- def chars
55
- ActiveSupport::Deprecation.warn('String#chars has been deprecated in favor of String#mb_chars.', caller)
56
- mb_chars
57
- end
58
- end
59
- else
60
- def mb_chars #:nodoc
61
- self
62
- end
63
-
64
47
  def is_utf8? #:nodoc
65
48
  case encoding
66
49
  when Encoding::UTF_8
@@ -71,5 +54,19 @@ class String
71
54
  false
72
55
  end
73
56
  end
57
+ else
58
+ def mb_chars
59
+ if ActiveSupport::Multibyte.proxy_class.wants?(self)
60
+ ActiveSupport::Multibyte.proxy_class.new(self)
61
+ else
62
+ self
63
+ end
64
+ end
65
+
66
+ # Returns true if the string has UTF-8 semantics (a String used for purely byte resources is unlikely to have
67
+ # them), returns false otherwise.
68
+ def is_utf8?
69
+ ActiveSupport::Multibyte::Chars.consumes?(self)
70
+ end
74
71
  end
75
72
  end
@@ -1,6 +1,7 @@
1
1
  require 'active_support/duration'
2
2
  require 'active_support/core_ext/date/acts_like'
3
3
  require 'active_support/core_ext/date/calculations'
4
+ require 'active_support/core_ext/date_time/conversions'
4
5
 
5
6
  class Time
6
7
  COMMON_YEAR_DAYS_IN_MONTH = [nil, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31]
@@ -23,10 +24,11 @@ class Time
23
24
  # (i.e., if year is within either 1970..2038 or 1902..2038, depending on system architecture);
24
25
  # otherwise returns a DateTime
25
26
  def time_with_datetime_fallback(utc_or_local, year, month=1, day=1, hour=0, min=0, sec=0, usec=0)
26
- ::Time.send(utc_or_local, year, month, day, hour, min, sec, usec)
27
+ time = ::Time.send(utc_or_local, year, month, day, hour, min, sec, usec)
28
+ # This check is needed because Time.utc(y) returns a time object in the 2000s for 0 <= y <= 138.
29
+ time.year == year ? time : ::DateTime.civil_from_format(utc_or_local, year, month, day, hour, min, sec)
27
30
  rescue
28
- offset = utc_or_local.to_sym == :local ? ::DateTime.local_offset : 0
29
- ::DateTime.civil(year, month, day, hour, min, sec, offset)
31
+ ::DateTime.civil_from_format(utc_or_local, year, month, day, hour, min, sec)
30
32
  end
31
33
 
32
34
  # Wraps class method +time_with_datetime_fallback+ with +utc_or_local+ set to <tt>:utc</tt>.
@@ -131,7 +133,7 @@ class Time
131
133
  end
132
134
 
133
135
  # Short-hand for years_ago(1)
134
- def last_year
136
+ def prev_year
135
137
  years_ago(1)
136
138
  end
137
139
 
@@ -140,9 +142,8 @@ class Time
140
142
  years_since(1)
141
143
  end
142
144
 
143
-
144
145
  # Short-hand for months_ago(1)
145
- def last_month
146
+ def prev_month
146
147
  months_ago(1)
147
148
  end
148
149
 
@@ -1,15 +1,20 @@
1
+ # encoding: utf-8
2
+
1
3
  if RUBY_VERSION >= '1.9'
2
4
  require 'uri'
3
5
 
4
6
  str = "\xE6\x97\xA5\xE6\x9C\xAC\xE8\xAA\x9E" # Ni-ho-nn-go in UTF-8, means Japanese.
5
- str.force_encoding(Encoding::UTF_8) if str.respond_to?(:force_encoding)
6
7
 
7
8
  parser = URI::Parser.new
9
+
8
10
  unless str == parser.unescape(parser.escape(str))
9
11
  URI::Parser.class_eval do
10
12
  remove_method :unescape
11
- def unescape(str, escaped = @regexp[:ESCAPED])
12
- enc = (str.encoding == Encoding::US_ASCII) ? Encoding::UTF_8 : str.encoding
13
+ def unescape(str, escaped = /%[a-fA-F\d]{2}/)
14
+ # TODO: Are we actually sure that ASCII == UTF-8?
15
+ # YK: My initial experiments say yes, but let's be sure please
16
+ enc = str.encoding
17
+ enc = Encoding::UTF_8 if enc == Encoding::US_ASCII
13
18
  str.gsub(escaped) { [$&[1, 2].hex].pack('C') }.force_encoding(enc)
14
19
  end
15
20
  end
@@ -47,6 +47,9 @@ module ActiveSupport #:nodoc:
47
47
  mattr_accessor :autoloaded_constants
48
48
  self.autoloaded_constants = []
49
49
 
50
+ mattr_accessor :references
51
+ self.references = {}
52
+
50
53
  # An array of constant names that need to be unloaded on every request. Used
51
54
  # to allow arbitrary constants to be marked for unloading.
52
55
  mattr_accessor :explicitly_unloadable_constants
@@ -66,7 +69,7 @@ module ActiveSupport #:nodoc:
66
69
  end
67
70
 
68
71
  def self.locked(*methods)
69
- methods.each { |m| class_eval "def #{m}(*) lock { super } end" }
72
+ methods.each { |m| class_eval "def #{m}(*) lock { super } end", __FILE__, __LINE__ }
70
73
  end
71
74
 
72
75
  def get(key)
@@ -476,9 +479,37 @@ module ActiveSupport #:nodoc:
476
479
  def remove_unloadable_constants!
477
480
  autoloaded_constants.each { |const| remove_constant const }
478
481
  autoloaded_constants.clear
482
+ Reference.clear!
479
483
  explicitly_unloadable_constants.each { |const| remove_constant const }
480
484
  end
481
485
 
486
+ class Reference
487
+ @@constants = Hash.new { |h, k| h[k] = Inflector.constantize(k) }
488
+
489
+ attr_reader :name
490
+
491
+ def initialize(name)
492
+ @name = name.to_s
493
+ @@constants[@name] = name if name.respond_to?(:name)
494
+ end
495
+
496
+ def get
497
+ @@constants[@name]
498
+ end
499
+
500
+ def self.clear!
501
+ @@constants.clear
502
+ end
503
+ end
504
+
505
+ def ref(name)
506
+ references[name] ||= Reference.new(name)
507
+ end
508
+
509
+ def constantize(name)
510
+ ref(name).get
511
+ end
512
+
482
513
  # Determine if the given constant has been automatically loaded.
483
514
  def autoloaded?(desc)
484
515
  # No name => anonymous module.
@@ -572,6 +603,7 @@ module ActiveSupport #:nodoc:
572
603
 
573
604
  log "removing constant #{const}"
574
605
  parent.instance_eval { remove_const to_remove }
606
+
575
607
  return true
576
608
  end
577
609
 
@@ -38,6 +38,7 @@ module ActiveSupport
38
38
  def is_a?(klass) #:nodoc:
39
39
  Duration == klass || value.is_a?(klass)
40
40
  end
41
+ alias :kind_of? :is_a?
41
42
 
42
43
  # Returns true if <tt>other</tt> is also a Duration instance with the
43
44
  # same <tt>value</tt>, or if <tt>other == value</tt>.
@@ -1,3 +1,5 @@
1
+ require 'active_support/core_ext/hash/keys'
2
+
1
3
  # This class has dubious semantics and we only have it so that
2
4
  # people can write params[:key] instead of params['key']
3
5
  # and they get the same value for both keys.
@@ -112,7 +114,9 @@ module ActiveSupport
112
114
  end
113
115
 
114
116
  def stringify_keys!; self end
115
- def symbolize_keys!; self end
117
+ def stringify_keys; dup end
118
+ undef :symbolize_keys!
119
+ def symbolize_keys; to_hash.symbolize_keys end
116
120
  def to_options!; self end
117
121
 
118
122
  # Convert to a Hash with String keys.
@@ -1,3 +1,8 @@
1
- require 'i18n'
1
+ begin
2
+ require 'i18n'
3
+ rescue LoadError => e
4
+ $stderr.puts "You don't have i18n installed in your application. Please add it to your Gemfile and run bundle install"
5
+ raise e
6
+ end
2
7
  I18n.load_path << "#{File.dirname(__FILE__)}/locale/en.yml"
3
- ActiveSupport.run_load_hooks(:i18n)
8
+ ActiveSupport.run_load_hooks(:i18n)