activesupport 2.2.3 → 2.3.2

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 (94) hide show
  1. data/CHANGELOG +128 -89
  2. data/lib/active_support.rb +31 -33
  3. data/lib/active_support/backtrace_cleaner.rb +72 -0
  4. data/lib/active_support/buffered_logger.rb +9 -7
  5. data/lib/active_support/cache.rb +13 -8
  6. data/lib/active_support/cache/drb_store.rb +2 -3
  7. data/lib/active_support/cache/mem_cache_store.rb +6 -1
  8. data/lib/active_support/cache/strategy/local_cache.rb +104 -0
  9. data/lib/active_support/callbacks.rb +20 -21
  10. data/lib/active_support/core_ext.rb +1 -1
  11. data/lib/active_support/core_ext/array.rb +2 -0
  12. data/lib/active_support/core_ext/array/conversions.rb +26 -13
  13. data/lib/active_support/core_ext/array/wrapper.rb +24 -0
  14. data/lib/active_support/core_ext/benchmark.rb +13 -6
  15. data/lib/active_support/core_ext/cgi/escape_skipping_slashes.rb +14 -5
  16. data/lib/active_support/core_ext/class/attribute_accessors.rb +24 -24
  17. data/lib/active_support/core_ext/class/delegating_attributes.rb +20 -19
  18. data/lib/active_support/core_ext/class/inheritable_attributes.rb +34 -34
  19. data/lib/active_support/core_ext/date/conversions.rb +3 -3
  20. data/lib/active_support/core_ext/date_time/conversions.rb +1 -1
  21. data/lib/active_support/core_ext/enumerable.rb +9 -0
  22. data/lib/active_support/core_ext/exception.rb +12 -8
  23. data/lib/active_support/core_ext/file/atomic.rb +2 -2
  24. data/lib/active_support/core_ext/hash/conversions.rb +32 -54
  25. data/lib/active_support/core_ext/hash/indifferent_access.rb +6 -0
  26. data/lib/active_support/core_ext/hash/keys.rb +1 -1
  27. data/lib/active_support/core_ext/hash/slice.rb +8 -1
  28. data/lib/active_support/core_ext/logger.rb +8 -6
  29. data/lib/active_support/core_ext/module/aliasing.rb +3 -3
  30. data/lib/active_support/core_ext/module/attr_accessor_with_default.rb +4 -4
  31. data/lib/active_support/core_ext/module/attribute_accessors.rb +24 -24
  32. data/lib/active_support/core_ext/module/delegation.rb +29 -3
  33. data/lib/active_support/core_ext/module/synchronization.rb +5 -5
  34. data/lib/active_support/core_ext/object/conversions.rb +2 -1
  35. data/lib/active_support/core_ext/object/misc.rb +16 -0
  36. data/lib/active_support/core_ext/range/conversions.rb +1 -1
  37. data/lib/active_support/core_ext/rexml.rb +29 -24
  38. data/lib/active_support/core_ext/string/inflections.rb +3 -3
  39. data/lib/active_support/core_ext/time/calculations.rb +1 -2
  40. data/lib/active_support/core_ext/time/conversions.rb +1 -1
  41. data/lib/active_support/core_ext/try.rb +36 -0
  42. data/lib/active_support/dependencies.rb +18 -14
  43. data/lib/active_support/deprecation.rb +10 -57
  44. data/lib/active_support/duration.rb +3 -1
  45. data/lib/active_support/inflections.rb +1 -0
  46. data/lib/active_support/inflector.rb +16 -7
  47. data/lib/active_support/json/decoding.rb +21 -3
  48. data/lib/active_support/json/encoders/date.rb +1 -1
  49. data/lib/active_support/json/encoders/date_time.rb +1 -1
  50. data/lib/active_support/json/encoders/hash.rb +10 -11
  51. data/lib/active_support/json/encoders/time.rb +1 -1
  52. data/lib/active_support/json/encoding.rb +23 -29
  53. data/lib/active_support/locale/en.yml +3 -2
  54. data/lib/active_support/memoizable.rb +61 -43
  55. data/lib/active_support/message_encryptor.rb +70 -0
  56. data/lib/active_support/message_verifier.rb +46 -0
  57. data/lib/active_support/multibyte.rb +6 -30
  58. data/lib/active_support/multibyte/chars.rb +30 -9
  59. data/lib/active_support/multibyte/unicode_database.rb +4 -4
  60. data/lib/active_support/option_merger.rb +7 -1
  61. data/lib/active_support/ordered_hash.rb +75 -27
  62. data/lib/active_support/secure_random.rb +8 -6
  63. data/lib/active_support/test_case.rb +32 -17
  64. data/lib/active_support/testing/{core_ext/test/unit/assertions.rb → assertions.rb} +13 -20
  65. data/lib/active_support/testing/declarative.rb +21 -0
  66. data/lib/active_support/testing/deprecation.rb +55 -0
  67. data/lib/active_support/testing/performance.rb +1 -1
  68. data/lib/active_support/testing/setup_and_teardown.rb +57 -86
  69. data/lib/active_support/time_with_zone.rb +8 -6
  70. data/lib/active_support/values/time_zone.rb +1 -0
  71. data/lib/active_support/vendor.rb +6 -11
  72. data/lib/active_support/vendor/i18n-0.1.3/MIT-LICENSE +20 -0
  73. data/lib/active_support/vendor/i18n-0.1.3/README.textile +20 -0
  74. data/lib/active_support/vendor/i18n-0.1.3/Rakefile +5 -0
  75. data/lib/active_support/vendor/i18n-0.1.3/i18n.gemspec +27 -0
  76. data/lib/active_support/vendor/{i18n-0.0.1 → i18n-0.1.3/lib}/i18n.rb +42 -37
  77. data/lib/active_support/vendor/{i18n-0.0.1 → i18n-0.1.3/lib}/i18n/backend/simple.rb +37 -39
  78. data/lib/active_support/vendor/{i18n-0.0.1 → i18n-0.1.3/lib}/i18n/exceptions.rb +3 -3
  79. data/lib/active_support/vendor/i18n-0.1.3/test/all.rb +5 -0
  80. data/lib/active_support/vendor/i18n-0.1.3/test/i18n_exceptions_test.rb +100 -0
  81. data/lib/active_support/vendor/i18n-0.1.3/test/i18n_test.rb +125 -0
  82. data/lib/active_support/vendor/i18n-0.1.3/test/locale/en.rb +1 -0
  83. data/lib/active_support/vendor/i18n-0.1.3/test/locale/en.yml +3 -0
  84. data/lib/active_support/vendor/i18n-0.1.3/test/simple_backend_test.rb +568 -0
  85. data/lib/active_support/vendor/{memcache-client-1.5.1 → memcache-client-1.6.5}/memcache.rb +381 -295
  86. data/lib/active_support/version.rb +2 -2
  87. data/lib/active_support/xml_mini.rb +31 -0
  88. data/lib/active_support/xml_mini/libxml.rb +133 -0
  89. data/lib/active_support/xml_mini/nokogiri.rb +77 -0
  90. data/lib/active_support/xml_mini/rexml.rb +108 -0
  91. metadata +85 -14
  92. data/lib/active_support/multibyte/utils.rb +0 -61
  93. data/lib/active_support/testing/core_ext/test.rb +0 -6
  94. data/lib/active_support/vendor/xml-simple-1.0.11/xmlsimple.rb +0 -1021
@@ -28,5 +28,6 @@ en:
28
28
  # Used in array.to_sentence.
29
29
  support:
30
30
  array:
31
- sentence_connector: "and"
32
- skip_last_comma: false
31
+ words_connector: ", "
32
+ two_words_connector: " and "
33
+ last_word_connector: ", and "
@@ -1,10 +1,10 @@
1
1
  module ActiveSupport
2
2
  module Memoizable
3
- MEMOIZED_IVAR = Proc.new do |symbol|
3
+ def self.memoized_ivar_for(symbol)
4
4
  "@_memoized_#{symbol.to_s.sub(/\?\Z/, '_query').sub(/!\Z/, '_bang')}".to_sym
5
5
  end
6
6
 
7
- module Freezable
7
+ module InstanceMethods
8
8
  def self.included(base)
9
9
  base.class_eval do
10
10
  unless base.method_defined?(:freeze_without_memoizable)
@@ -19,23 +19,35 @@ module ActiveSupport
19
19
  end
20
20
 
21
21
  def memoize_all
22
- methods.each do |m|
23
- if m.to_s =~ /^_unmemoized_(.*)/
24
- if method(m).arity == 0
25
- __send__($1)
26
- else
27
- ivar = MEMOIZED_IVAR.call($1)
28
- instance_variable_set(ivar, {})
22
+ prime_cache ".*"
23
+ end
24
+
25
+ def unmemoize_all
26
+ flush_cache ".*"
27
+ end
28
+
29
+ def prime_cache(*syms)
30
+ syms.each do |sym|
31
+ methods.each do |m|
32
+ if m.to_s =~ /^_unmemoized_(#{sym})/
33
+ if method(m).arity == 0
34
+ __send__($1)
35
+ else
36
+ ivar = ActiveSupport::Memoizable.memoized_ivar_for($1)
37
+ instance_variable_set(ivar, {})
38
+ end
29
39
  end
30
40
  end
31
41
  end
32
42
  end
33
43
 
34
- def unmemoize_all
35
- methods.each do |m|
36
- if m.to_s =~ /^_unmemoized_(.*)/
37
- ivar = MEMOIZED_IVAR.call($1)
38
- instance_variable_get(ivar).clear if instance_variable_defined?(ivar)
44
+ def flush_cache(*syms, &block)
45
+ syms.each do |sym|
46
+ methods.each do |m|
47
+ if m.to_s =~ /^_unmemoized_(#{sym})/
48
+ ivar = ActiveSupport::Memoizable.memoized_ivar_for($1)
49
+ instance_variable_get(ivar).clear if instance_variable_defined?(ivar)
50
+ end
39
51
  end
40
52
  end
41
53
  end
@@ -44,37 +56,43 @@ module ActiveSupport
44
56
  def memoize(*symbols)
45
57
  symbols.each do |symbol|
46
58
  original_method = :"_unmemoized_#{symbol}"
47
- memoized_ivar = MEMOIZED_IVAR.call(symbol)
59
+ memoized_ivar = ActiveSupport::Memoizable.memoized_ivar_for(symbol)
48
60
 
49
61
  class_eval <<-EOS, __FILE__, __LINE__
50
- include Freezable
51
-
52
- raise "Already memoized #{symbol}" if method_defined?(:#{original_method})
53
- alias #{original_method} #{symbol}
54
-
55
- if instance_method(:#{symbol}).arity == 0
56
- def #{symbol}(reload = false)
57
- if reload || !defined?(#{memoized_ivar}) || #{memoized_ivar}.empty?
58
- #{memoized_ivar} = [#{original_method}.freeze]
59
- end
60
- #{memoized_ivar}[0]
61
- end
62
- else
63
- def #{symbol}(*args)
64
- #{memoized_ivar} ||= {} unless frozen?
65
- reload = args.pop if args.last == true || args.last == :reload
66
-
67
- if defined?(#{memoized_ivar}) && #{memoized_ivar}
68
- if !reload && #{memoized_ivar}.has_key?(args)
69
- #{memoized_ivar}[args]
70
- elsif #{memoized_ivar}
71
- #{memoized_ivar}[args] = #{original_method}(*args).freeze
72
- end
73
- else
74
- #{original_method}(*args)
75
- end
76
- end
77
- end
62
+ include InstanceMethods # include InstanceMethods
63
+ #
64
+ if method_defined?(:#{original_method}) # if method_defined?(:_unmemoized_mime_type)
65
+ raise "Already memoized #{symbol}" # raise "Already memoized mime_type"
66
+ end # end
67
+ alias #{original_method} #{symbol} # alias _unmemoized_mime_type mime_type
68
+ #
69
+ if instance_method(:#{symbol}).arity == 0 # if instance_method(:mime_type).arity == 0
70
+ def #{symbol}(reload = false) # def mime_type(reload = false)
71
+ if reload || !defined?(#{memoized_ivar}) || #{memoized_ivar}.empty? # if reload || !defined?(@_memoized_mime_type) || @_memoized_mime_type.empty?
72
+ #{memoized_ivar} = [#{original_method}.freeze] # @_memoized_mime_type = [_unmemoized_mime_type.freeze]
73
+ end # end
74
+ #{memoized_ivar}[0] # @_memoized_mime_type[0]
75
+ end # end
76
+ else # else
77
+ def #{symbol}(*args) # def mime_type(*args)
78
+ #{memoized_ivar} ||= {} unless frozen? # @_memoized_mime_type ||= {} unless frozen?
79
+ reload = args.pop if args.last == true || args.last == :reload # reload = args.pop if args.last == true || args.last == :reload
80
+ #
81
+ if defined?(#{memoized_ivar}) && #{memoized_ivar} # if defined?(@_memoized_mime_type) && @_memoized_mime_type
82
+ if !reload && #{memoized_ivar}.has_key?(args) # if !reload && @_memoized_mime_type.has_key?(args)
83
+ #{memoized_ivar}[args] # @_memoized_mime_type[args]
84
+ elsif #{memoized_ivar} # elsif @_memoized_mime_type
85
+ #{memoized_ivar}[args] = #{original_method}(*args).freeze # @_memoized_mime_type[args] = _unmemoized_mime_type(*args).freeze
86
+ end # end
87
+ else # else
88
+ #{original_method}(*args) # _unmemoized_mime_type(*args)
89
+ end # end
90
+ end # end
91
+ end # end
92
+ #
93
+ if private_method_defined?(#{original_method.inspect}) # if private_method_defined?(:_unmemoized_mime_type)
94
+ private #{symbol.inspect} # private :mime_type
95
+ end # end
78
96
  EOS
79
97
  end
80
98
  end
@@ -0,0 +1,70 @@
1
+ require 'openssl'
2
+
3
+ module ActiveSupport
4
+ # MessageEncryptor is a simple way to encrypt values which get stored somewhere
5
+ # you don't trust.
6
+ #
7
+ # The cipher text and initialization vector are base64 encoded and returned to you.
8
+ #
9
+ # This can be used in situations similar to the MessageVerifier, but where you don't
10
+ # want users to be able to determine the value of the payload.
11
+ class MessageEncryptor
12
+ class InvalidMessage < StandardError; end
13
+ OpenSSLCipherError = OpenSSL::Cipher.const_defined?(:CipherError) ? OpenSSL::Cipher::CipherError : OpenSSL::CipherError
14
+
15
+ def initialize(secret, cipher = 'aes-256-cbc')
16
+ @secret = secret
17
+ @cipher = cipher
18
+ end
19
+
20
+ def encrypt(value)
21
+ cipher = new_cipher
22
+ # Rely on OpenSSL for the initialization vector
23
+ iv = cipher.random_iv
24
+
25
+ cipher.encrypt
26
+ cipher.key = @secret
27
+ cipher.iv = iv
28
+
29
+ encrypted_data = cipher.update(Marshal.dump(value))
30
+ encrypted_data << cipher.final
31
+
32
+ [encrypted_data, iv].map {|v| ActiveSupport::Base64.encode64s(v)}.join("--")
33
+ end
34
+
35
+ def decrypt(encrypted_message)
36
+ cipher = new_cipher
37
+ encrypted_data, iv = encrypted_message.split("--").map {|v| ActiveSupport::Base64.decode64(v)}
38
+
39
+ cipher.decrypt
40
+ cipher.key = @secret
41
+ cipher.iv = iv
42
+
43
+ decrypted_data = cipher.update(encrypted_data)
44
+ decrypted_data << cipher.final
45
+
46
+ Marshal.load(decrypted_data)
47
+ rescue OpenSSLCipherError, TypeError
48
+ raise InvalidMessage
49
+ end
50
+
51
+ def encrypt_and_sign(value)
52
+ verifier.generate(encrypt(value))
53
+ end
54
+
55
+ def decrypt_and_verify(value)
56
+ decrypt(verifier.verify(value))
57
+ end
58
+
59
+
60
+
61
+ private
62
+ def new_cipher
63
+ OpenSSL::Cipher::Cipher.new(@cipher)
64
+ end
65
+
66
+ def verifier
67
+ MessageVerifier.new(@secret)
68
+ end
69
+ end
70
+ end
@@ -0,0 +1,46 @@
1
+ module ActiveSupport
2
+ # MessageVerifier makes it easy to generate and verify messages which are signed
3
+ # to prevent tampering.
4
+ #
5
+ # This is useful for cases like remember-me tokens and auto-unsubscribe links where the
6
+ # session store isn't suitable or available.
7
+ #
8
+ # Remember Me:
9
+ # cookies[:remember_me] = @verifier.generate([@user.id, 2.weeks.from_now])
10
+ #
11
+ # In the authentication filter:
12
+ #
13
+ # id, time = @verifier.verify(cookies[:remember_me])
14
+ # if time < Time.now
15
+ # self.current_user = User.find(id)
16
+ # end
17
+ #
18
+ class MessageVerifier
19
+ class InvalidSignature < StandardError; end
20
+
21
+ def initialize(secret, digest = 'SHA1')
22
+ @secret = secret
23
+ @digest = digest
24
+ end
25
+
26
+ def verify(signed_message)
27
+ data, digest = signed_message.split("--")
28
+ if digest != generate_digest(data)
29
+ raise InvalidSignature
30
+ else
31
+ Marshal.load(ActiveSupport::Base64.decode64(data))
32
+ end
33
+ end
34
+
35
+ def generate(value)
36
+ data = ActiveSupport::Base64.encode64s(Marshal.dump(value))
37
+ "#{data}--#{generate_digest(data)}"
38
+ end
39
+
40
+ private
41
+ def generate_digest(data)
42
+ require 'openssl' unless defined?(OpenSSL)
43
+ OpenSSL::HMAC.hexdigest(OpenSSL::Digest::Digest.new(@digest), @secret, data)
44
+ end
45
+ end
46
+ end
@@ -1,5 +1,9 @@
1
1
  # encoding: utf-8
2
2
 
3
+ require 'active_support/multibyte/chars'
4
+ require 'active_support/multibyte/exceptions'
5
+ require 'active_support/multibyte/unicode_database'
6
+
3
7
  module ActiveSupport #:nodoc:
4
8
  module Multibyte
5
9
  # A list of all available normalization forms. See http://www.unicode.org/reports/tr15/tr15-29.html for more
@@ -23,35 +27,7 @@ module ActiveSupport #:nodoc:
23
27
  #
24
28
  # Example:
25
29
  # ActiveSupport::Multibyte.proxy_class = CharsForUTF32
26
- def self.proxy_class=(klass)
27
- @proxy_class = klass
28
- end
29
-
30
- # Returns the currect proxy class
31
- def self.proxy_class
32
- @proxy_class ||= ActiveSupport::Multibyte::Chars
33
- end
34
-
35
- # Regular expressions that describe valid byte sequences for a character
36
- VALID_CHARACTER = {
37
- # Borrowed from the Kconv library by Shinji KONO - (also as seen on the W3C site)
38
- 'UTF-8' => /\A(?:
39
- [\x00-\x7f] |
40
- [\xc2-\xdf] [\x80-\xbf] |
41
- \xe0 [\xa0-\xbf] [\x80-\xbf] |
42
- [\xe1-\xef] [\x80-\xbf] [\x80-\xbf] |
43
- \xf0 [\x90-\xbf] [\x80-\xbf] [\x80-\xbf] |
44
- [\xf1-\xf3] [\x80-\xbf] [\x80-\xbf] [\x80-\xbf] |
45
- \xf4 [\x80-\x8f] [\x80-\xbf] [\x80-\xbf])\z /xn,
46
- # Quick check for valid Shift-JIS characters, disregards the odd-even pairing
47
- 'Shift_JIS' => /\A(?:
48
- [\x00-\x7e \xa1-\xdf] |
49
- [\x81-\x9f \xe0-\xef] [\x40-\x7e \x80-\x9e \x9f-\xfc])\z /xn
50
- }
30
+ mattr_accessor :proxy_class
31
+ self.proxy_class = ActiveSupport::Multibyte::Chars
51
32
  end
52
33
  end
53
-
54
- require 'active_support/multibyte/chars'
55
- require 'active_support/multibyte/exceptions'
56
- require 'active_support/multibyte/unicode_database'
57
- require 'active_support/multibyte/utils'
@@ -73,7 +73,16 @@ module ActiveSupport #:nodoc:
73
73
  UNICODE_TRAILERS_PAT = /(#{codepoints_to_pattern(UNICODE_LEADERS_AND_TRAILERS)})+\Z/
74
74
  UNICODE_LEADERS_PAT = /\A(#{codepoints_to_pattern(UNICODE_LEADERS_AND_TRAILERS)})+/
75
75
 
76
- UTF8_PAT = ActiveSupport::Multibyte::VALID_CHARACTER['UTF-8']
76
+ # Borrowed from the Kconv library by Shinji KONO - (also as seen on the W3C site)
77
+ UTF8_PAT = /\A(?:
78
+ [\x00-\x7f] |
79
+ [\xc2-\xdf] [\x80-\xbf] |
80
+ \xe0 [\xa0-\xbf] [\x80-\xbf] |
81
+ [\xe1-\xef] [\x80-\xbf] [\x80-\xbf] |
82
+ \xf0 [\x90-\xbf] [\x80-\xbf] [\x80-\xbf] |
83
+ [\xf1-\xf3] [\x80-\xbf] [\x80-\xbf] [\x80-\xbf] |
84
+ \xf4 [\x80-\x8f] [\x80-\xbf] [\x80-\xbf]
85
+ )*\z/xn
77
86
 
78
87
  attr_reader :wrapped_string
79
88
  alias to_s wrapped_string
@@ -283,23 +292,23 @@ module ActiveSupport #:nodoc:
283
292
  def rstrip
284
293
  chars(@wrapped_string.gsub(UNICODE_TRAILERS_PAT, ''))
285
294
  end
286
-
295
+
287
296
  # Strips entire range of Unicode whitespace from the left of the string.
288
297
  def lstrip
289
298
  chars(@wrapped_string.gsub(UNICODE_LEADERS_PAT, ''))
290
299
  end
291
-
300
+
292
301
  # Strips entire range of Unicode whitespace from the right and left of the string.
293
302
  def strip
294
303
  rstrip.lstrip
295
304
  end
296
-
305
+
297
306
  # Returns the number of codepoints in the string
298
307
  def size
299
308
  self.class.u_unpack(@wrapped_string).size
300
309
  end
301
310
  alias_method :length, :size
302
-
311
+
303
312
  # Reverses all characters in the string.
304
313
  #
305
314
  # Example:
@@ -307,7 +316,7 @@ module ActiveSupport #:nodoc:
307
316
  def reverse
308
317
  chars(self.class.u_unpack(@wrapped_string).reverse.pack('U*'))
309
318
  end
310
-
319
+
311
320
  # Implements Unicode-aware slice with codepoints. Slicing on one point returns the codepoints for that
312
321
  # character.
313
322
  #
@@ -335,7 +344,19 @@ module ActiveSupport #:nodoc:
335
344
  end
336
345
  alias_method :[], :slice
337
346
 
338
- # Converts first character in the string to Unicode value
347
+ # Like <tt>String#slice!</tt>, except instead of byte offsets you specify character offsets.
348
+ #
349
+ # Example:
350
+ # s = 'こんにちは'
351
+ # s.mb_chars.slice!(2..3).to_s #=> "にち"
352
+ # s #=> "こんは"
353
+ def slice!(*args)
354
+ slice = self[*args]
355
+ self[*args] = ''
356
+ slice
357
+ end
358
+
359
+ # Returns the codepoint of the first character in the string.
339
360
  #
340
361
  # Example:
341
362
  # 'こんにちは'.mb_chars.ord #=> 12371
@@ -423,7 +444,7 @@ module ActiveSupport #:nodoc:
423
444
  chars(self.class.tidy_bytes(@wrapped_string))
424
445
  end
425
446
 
426
- %w(lstrip rstrip strip reverse upcase downcase slice tidy_bytes capitalize).each do |method|
447
+ %w(lstrip rstrip strip reverse upcase downcase tidy_bytes capitalize).each do |method|
427
448
  define_method("#{method}!") do |*args|
428
449
  unless args.nil?
429
450
  @wrapped_string = send(method, *args).to_s
@@ -610,7 +631,7 @@ module ActiveSupport #:nodoc:
610
631
  string.split(//u).map do |c|
611
632
  c.force_encoding(Encoding::ASCII) if c.respond_to?(:force_encoding)
612
633
 
613
- if !ActiveSupport::Multibyte::VALID_CHARACTER['UTF-8'].match(c)
634
+ if !UTF8_PAT.match(c)
614
635
  n = c.unpack('C')[0]
615
636
  n < 128 ? n.chr :
616
637
  n < 160 ? [UCD.cp1252[n] || n].pack('U') :
@@ -24,10 +24,10 @@ module ActiveSupport #:nodoc:
24
24
  # Lazy load the Unicode database so it's only loaded when it's actually used
25
25
  ATTRIBUTES.each do |attr_name|
26
26
  class_eval(<<-EOS, __FILE__, __LINE__)
27
- def #{attr_name}
28
- load
29
- @#{attr_name}
30
- end
27
+ def #{attr_name} # def codepoints
28
+ load # load
29
+ @#{attr_name} # @codepoints
30
+ end # end
31
31
  EOS
32
32
  end
33
33
 
@@ -10,7 +10,13 @@ module ActiveSupport
10
10
 
11
11
  private
12
12
  def method_missing(method, *arguments, &block)
13
- arguments << (arguments.last.respond_to?(:to_hash) ? @options.deep_merge(arguments.pop) : @options.dup)
13
+ if arguments.last.is_a?(Proc)
14
+ proc = arguments.pop
15
+ arguments << lambda { |*args| @options.deep_merge(proc.call(*args)) }
16
+ else
17
+ arguments << (arguments.last.respond_to?(:to_hash) ? @options.deep_merge(arguments.pop) : @options.dup)
18
+ end
19
+
14
20
  @context.__send__(method, *arguments, &block)
15
21
  end
16
22
  end
@@ -4,55 +4,103 @@ module ActiveSupport
4
4
  if RUBY_VERSION >= '1.9'
5
5
  OrderedHash = ::Hash
6
6
  else
7
- class OrderedHash < Array #:nodoc:
7
+ class OrderedHash < Hash #:nodoc:
8
+ def initialize(*args, &block)
9
+ super
10
+ @keys = []
11
+ end
12
+
13
+ def initialize_copy(other)
14
+ super
15
+ # make a deep copy of keys
16
+ @keys = other.keys
17
+ end
18
+
8
19
  def []=(key, value)
9
- if pair = assoc(key)
10
- pair.pop
11
- pair << value
12
- else
13
- self << [key, value]
20
+ @keys << key if !has_key?(key)
21
+ super
22
+ end
23
+
24
+ def delete(key)
25
+ if has_key? key
26
+ index = @keys.index(key)
27
+ @keys.delete_at index
14
28
  end
15
- value
29
+ super
30
+ end
31
+
32
+ def delete_if
33
+ super
34
+ sync_keys!
35
+ self
16
36
  end
17
37
 
18
- def [](key)
19
- pair = assoc(key)
20
- pair ? pair.last : nil
38
+ def reject!
39
+ super
40
+ sync_keys!
41
+ self
21
42
  end
22
43
 
23
- def delete(key)
24
- pair = assoc(key)
25
- pair ? array_index = index(pair) : nil
26
- array_index ? delete_at(array_index).last : nil
44
+ def reject(&block)
45
+ dup.reject!(&block)
27
46
  end
28
47
 
29
48
  def keys
30
- collect { |key, value| key }
49
+ @keys.dup
31
50
  end
32
51
 
33
52
  def values
34
- collect { |key, value| value }
53
+ @keys.collect { |key| self[key] }
35
54
  end
36
55
 
37
56
  def to_hash
38
- returning({}) do |hash|
39
- each { |array| hash[array[0]] = array[1] }
40
- end
57
+ self
58
+ end
59
+
60
+ def each_key
61
+ @keys.each { |key| yield key }
41
62
  end
42
63
 
43
- def has_key?(k)
44
- !assoc(k).nil?
64
+ def each_value
65
+ @keys.each { |key| yield self[key]}
45
66
  end
46
67
 
47
- alias_method :key?, :has_key?
48
- alias_method :include?, :has_key?
49
- alias_method :member?, :has_key?
68
+ def each
69
+ @keys.each {|key| yield [key, self[key]]}
70
+ end
71
+
72
+ alias_method :each_pair, :each
73
+
74
+ def clear
75
+ super
76
+ @keys.clear
77
+ self
78
+ end
79
+
80
+ def shift
81
+ k = @keys.first
82
+ v = delete(k)
83
+ [k, v]
84
+ end
50
85
 
51
- def has_value?(v)
52
- any? { |key, value| value == v }
86
+ def merge!(other_hash)
87
+ other_hash.each {|k,v| self[k] = v }
88
+ self
53
89
  end
54
90
 
55
- alias_method :value?, :has_value?
91
+ def merge(other_hash)
92
+ dup.merge!(other_hash)
93
+ end
94
+
95
+ def inspect
96
+ "#<OrderedHash #{super}>"
97
+ end
98
+
99
+ private
100
+
101
+ def sync_keys!
102
+ @keys.delete_if {|k| !has_key?(k)}
103
+ end
56
104
  end
57
105
  end
58
106
  end