activesupport 1.4.4 → 2.0.0

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 (126) hide show
  1. data/CHANGELOG +263 -7
  2. data/lib/active_support.rb +9 -4
  3. data/lib/active_support/basic_object.rb +5 -0
  4. data/lib/active_support/buffered_logger.rb +107 -0
  5. data/lib/active_support/clean_logger.rb +94 -5
  6. data/lib/active_support/core_ext.rb +4 -1
  7. data/lib/active_support/core_ext/array.rb +8 -2
  8. data/lib/active_support/core_ext/array/access.rb +28 -0
  9. data/lib/active_support/core_ext/array/conversions.rb +28 -15
  10. data/lib/active_support/core_ext/array/extract_options.rb +19 -0
  11. data/lib/active_support/core_ext/array/grouping.rb +20 -7
  12. data/lib/active_support/core_ext/array/random_access.rb +12 -0
  13. data/lib/active_support/core_ext/bigdecimal.rb +1 -2
  14. data/lib/active_support/core_ext/bigdecimal/{formatting.rb → conversions.rb} +1 -2
  15. data/lib/active_support/core_ext/blank.rb +2 -8
  16. data/lib/active_support/core_ext/cgi.rb +2 -2
  17. data/lib/active_support/core_ext/class.rb +4 -3
  18. data/lib/active_support/core_ext/class/attribute_accessors.rb +1 -1
  19. data/lib/active_support/core_ext/class/delegating_attributes.rb +40 -0
  20. data/lib/active_support/core_ext/class/inheritable_attributes.rb +3 -3
  21. data/lib/active_support/core_ext/class/removal.rb +2 -2
  22. data/lib/active_support/core_ext/date.rb +5 -1
  23. data/lib/active_support/core_ext/date/behavior.rb +13 -0
  24. data/lib/active_support/core_ext/date/calculations.rb +188 -0
  25. data/lib/active_support/core_ext/date/conversions.rb +69 -13
  26. data/lib/active_support/core_ext/date_time.rb +10 -0
  27. data/lib/active_support/core_ext/date_time/calculations.rb +77 -0
  28. data/lib/active_support/core_ext/date_time/conversions.rb +54 -0
  29. data/lib/active_support/core_ext/duplicable.rb +37 -0
  30. data/lib/active_support/core_ext/enumerable.rb +1 -0
  31. data/lib/active_support/core_ext/exception.rb +2 -2
  32. data/lib/active_support/core_ext/file.rb +21 -0
  33. data/lib/active_support/core_ext/float.rb +5 -0
  34. data/lib/active_support/core_ext/float/rounding.rb +24 -0
  35. data/lib/active_support/core_ext/hash.rb +5 -5
  36. data/lib/active_support/core_ext/hash/conversions.rb +86 -34
  37. data/lib/active_support/core_ext/hash/diff.rb +8 -0
  38. data/lib/active_support/core_ext/hash/except.rb +24 -0
  39. data/lib/active_support/core_ext/hash/indifferent_access.rb +15 -2
  40. data/lib/active_support/core_ext/hash/keys.rb +10 -3
  41. data/lib/active_support/core_ext/hash/reverse_merge.rb +2 -2
  42. data/lib/active_support/core_ext/hash/slice.rb +28 -0
  43. data/lib/active_support/core_ext/integer.rb +2 -2
  44. data/lib/active_support/core_ext/kernel.rb +5 -4
  45. data/lib/active_support/core_ext/kernel/debugger.rb +13 -0
  46. data/lib/active_support/core_ext/module.rb +8 -7
  47. data/lib/active_support/core_ext/module/aliasing.rb +17 -5
  48. data/lib/active_support/core_ext/module/attr_accessor_with_default.rb +31 -0
  49. data/lib/active_support/core_ext/module/attribute_accessors.rb +1 -1
  50. data/lib/active_support/core_ext/module/delegation.rb +21 -0
  51. data/lib/active_support/core_ext/name_error.rb +2 -2
  52. data/lib/active_support/core_ext/numeric.rb +2 -2
  53. data/lib/active_support/core_ext/numeric/time.rb +30 -11
  54. data/lib/active_support/core_ext/object.rb +3 -2
  55. data/lib/active_support/core_ext/object/extending.rb +40 -29
  56. data/lib/active_support/core_ext/object/instance_variables.rb +22 -0
  57. data/lib/active_support/core_ext/object/misc.rb +29 -4
  58. data/lib/active_support/core_ext/pathname.rb +1 -1
  59. data/lib/active_support/core_ext/range.rb +7 -1
  60. data/lib/active_support/core_ext/range/blockless_step.rb +22 -0
  61. data/lib/active_support/core_ext/range/conversions.rb +8 -6
  62. data/lib/active_support/core_ext/range/include_range.rb +22 -0
  63. data/lib/active_support/core_ext/range/overlaps.rb +12 -0
  64. data/lib/active_support/core_ext/string.rb +10 -7
  65. data/lib/active_support/core_ext/string/conversions.rb +5 -1
  66. data/lib/active_support/core_ext/string/unicode.rb +2 -2
  67. data/lib/active_support/core_ext/string/xchar.rb +11 -0
  68. data/lib/active_support/core_ext/symbol.rb +12 -10
  69. data/lib/active_support/core_ext/test.rb +1 -0
  70. data/lib/active_support/core_ext/test/unit/assertions.rb +62 -0
  71. data/lib/active_support/core_ext/time.rb +4 -2
  72. data/lib/active_support/core_ext/time/behavior.rb +13 -0
  73. data/lib/active_support/core_ext/time/calculations.rb +87 -54
  74. data/lib/active_support/core_ext/time/conversions.rb +71 -10
  75. data/lib/active_support/dependencies.rb +25 -24
  76. data/lib/active_support/deprecation.rb +4 -2
  77. data/lib/active_support/duration.rb +86 -0
  78. data/lib/active_support/inflections.rb +2 -1
  79. data/lib/active_support/inflector.rb +13 -6
  80. data/lib/active_support/json.rb +22 -39
  81. data/lib/active_support/json/decoding.rb +60 -0
  82. data/lib/active_support/json/encoders/date.rb +5 -0
  83. data/lib/active_support/json/encoders/date_time.rb +5 -0
  84. data/lib/active_support/json/encoders/enumerable.rb +12 -0
  85. data/lib/active_support/json/encoders/false_class.rb +5 -0
  86. data/lib/active_support/json/encoders/hash.rb +50 -0
  87. data/lib/active_support/json/encoders/nil_class.rb +5 -0
  88. data/lib/active_support/json/encoders/numeric.rb +5 -0
  89. data/lib/active_support/json/encoders/object.rb +6 -0
  90. data/lib/active_support/json/encoders/regexp.rb +5 -0
  91. data/lib/active_support/json/encoders/string.rb +30 -0
  92. data/lib/active_support/json/encoders/symbol.rb +5 -0
  93. data/lib/active_support/json/encoders/time.rb +5 -0
  94. data/lib/active_support/json/encoders/true_class.rb +5 -0
  95. data/lib/active_support/json/encoding.rb +38 -0
  96. data/lib/active_support/json/variable.rb +10 -0
  97. data/lib/active_support/multibyte.rb +7 -5
  98. data/lib/active_support/multibyte/chars.rb +6 -0
  99. data/lib/active_support/multibyte/handlers/utf8_handler.rb +115 -5
  100. data/lib/active_support/option_merger.rb +7 -7
  101. data/lib/active_support/ordered_options.rb +22 -17
  102. data/lib/active_support/test_case.rb +5 -0
  103. data/lib/active_support/testing.rb +1 -0
  104. data/lib/active_support/testing/default.rb +12 -0
  105. data/lib/active_support/values/time_zone.rb +3 -3
  106. data/lib/active_support/vendor.rb +14 -0
  107. data/lib/active_support/vendor/builder-2.1.2/blankslate.rb +113 -0
  108. data/lib/active_support/vendor/{builder.rb → builder-2.1.2/builder.rb} +0 -0
  109. data/lib/active_support/vendor/builder-2.1.2/builder/blankslate.rb +20 -0
  110. data/lib/active_support/vendor/builder-2.1.2/builder/css.rb +250 -0
  111. data/lib/active_support/vendor/{builder → builder-2.1.2/builder}/xchar.rb +11 -8
  112. data/lib/active_support/vendor/{builder → builder-2.1.2/builder}/xmlbase.rb +38 -44
  113. data/lib/active_support/vendor/{builder → builder-2.1.2/builder}/xmlevents.rb +1 -1
  114. data/lib/active_support/vendor/{builder → builder-2.1.2/builder}/xmlmarkup.rb +40 -39
  115. data/lib/active_support/vendor/{xml_simple.rb → xml-simple-1.0.11/xmlsimple.rb} +3 -3
  116. data/lib/active_support/version.rb +3 -3
  117. data/lib/active_support/whiny_nil.rb +12 -12
  118. data/lib/activesupport.rb +1 -0
  119. metadata +69 -17
  120. data/lib/active_support/binding_of_caller.rb +0 -84
  121. data/lib/active_support/breakpoint.rb +0 -528
  122. data/lib/active_support/caching_tools.rb +0 -62
  123. data/lib/active_support/json/encoders.rb +0 -25
  124. data/lib/active_support/json/encoders/core.rb +0 -70
  125. data/lib/active_support/reloadable.rb +0 -60
  126. data/lib/active_support/vendor/builder/blankslate.rb +0 -63
@@ -0,0 +1,60 @@
1
+ require 'yaml'
2
+ require 'strscan'
3
+
4
+ module ActiveSupport
5
+ module JSON
6
+ class ParseError < StandardError
7
+ end
8
+
9
+ class << self
10
+ # Converts a JSON string into a Ruby object.
11
+ def decode(json)
12
+ YAML.load(convert_json_to_yaml(json))
13
+ rescue ArgumentError => e
14
+ raise ParseError, "Invalid JSON string"
15
+ end
16
+
17
+ protected
18
+ # matches YAML-formatted dates
19
+ DATE_REGEX = /^\d{4}-\d{2}-\d{2}|\d{4}-\d{1,2}-\d{1,2}[ \t]+\d{1,2}:\d{2}:\d{2}(\.[0-9]*)?(([ \t]*)Z|[-+]\d{2}?(:\d{2})?)?$/
20
+
21
+ # Ensure that ":" and "," are always followed by a space
22
+ def convert_json_to_yaml(json) #:nodoc:
23
+ scanner, quoting, marks, pos, times = StringScanner.new(json), false, [], nil, []
24
+ while scanner.scan_until(/(\\['"]|['":,\\]|\\.)/)
25
+ case char = scanner[1]
26
+ when '"', "'"
27
+ if !quoting
28
+ quoting = char
29
+ pos = scanner.pos
30
+ elsif quoting == char
31
+ if json[pos..scanner.pos-2] =~ DATE_REGEX
32
+ # found a date, track the exact positions of the quotes so we can remove them later.
33
+ # oh, and increment them for each current mark, each one is an extra padded space that bumps
34
+ # the position in the final yaml output
35
+ total_marks = marks.size
36
+ times << pos+total_marks << scanner.pos+total_marks
37
+ end
38
+ quoting = false
39
+ end
40
+ when ":",","
41
+ marks << scanner.pos - 1 unless quoting
42
+ end
43
+ end
44
+
45
+ if marks.empty?
46
+ json.gsub(/\\\//, '/')
47
+ else
48
+ # FIXME: multiple slow enumerations
49
+ output = ([0] + marks.map(&:succ)).
50
+ zip(marks + [json.length]).
51
+ map { |left, right| json[left..right] }.
52
+ join(" ")
53
+ times.each { |i| output[i-1] = ' ' }
54
+ output.gsub!(/\\\//, '/')
55
+ output
56
+ end
57
+ end
58
+ end
59
+ end
60
+ end
@@ -0,0 +1,5 @@
1
+ class Date
2
+ def to_json(options = nil) #:nodoc:
3
+ %("#{strftime("%Y/%m/%d")}")
4
+ end
5
+ end
@@ -0,0 +1,5 @@
1
+ class DateTime
2
+ def to_json(options = nil) #:nodoc:
3
+ %("#{strftime("%Y/%m/%d %H:%M:%S %z")}")
4
+ end
5
+ end
@@ -0,0 +1,12 @@
1
+ module Enumerable
2
+ # Returns a JSON string representing the enumerable. Any +options+
3
+ # given will be passed on to its elements. For example:
4
+ #
5
+ # users = User.find(:all)
6
+ # users.to_json(:only => :name)
7
+ #
8
+ # will pass the <tt>:only => :name</tt> option to each user.
9
+ def to_json(options = {}) #:nodoc:
10
+ "[#{map { |value| ActiveSupport::JSON.encode(value, options) } * ', '}]"
11
+ end
12
+ end
@@ -0,0 +1,5 @@
1
+ class FalseClass
2
+ def to_json(options = nil) #:nodoc:
3
+ 'false'
4
+ end
5
+ end
@@ -0,0 +1,50 @@
1
+ class Hash
2
+ # Returns a JSON string representing the hash.
3
+ #
4
+ # Without any +options+, the returned JSON string will include all
5
+ # the hash keys. For example:
6
+ #
7
+ # { :name => "Konata Izumi", 'age' => 16, 1 => 2 }.to_json
8
+ #
9
+ # {"name": "Konata Izumi", 1: 2, "age": 16}
10
+ #
11
+ # The keys in the JSON string are unordered due to the nature of hashes.
12
+ #
13
+ # The <tt>:only</tt> and <tt>:except</tt> options can be used to limit the
14
+ # attributes included, and will accept 1 or more hash keys to include/exclude.
15
+ #
16
+ # { :name => "Konata Izumi", 'age' => 16, 1 => 2 }.to_json(:only => [:name, 'age'])
17
+ #
18
+ # {"name": "Konata Izumi", "age": 16}
19
+ #
20
+ # { :name => "Konata Izumi", 'age' => 16, 1 => 2 }.to_json(:except => 1)
21
+ #
22
+ # {"name": "Konata Izumi", "age": 16}
23
+ #
24
+ # The +options+ also filter down to any hash values. This is particularly
25
+ # useful for converting hashes containing ActiveRecord objects or any object
26
+ # that responds to options in their <tt>to_json</tt> method. For example:
27
+ #
28
+ # users = User.find(:all)
29
+ # { :users => users, :count => users.size }.to_json(:include => :posts)
30
+ #
31
+ # would pass the <tt>:include => :posts</tt> option to <tt>users</tt>,
32
+ # allowing the posts association in the User model to be converted to JSON
33
+ # as well.
34
+ def to_json(options = {}) #:nodoc:
35
+ hash_keys = self.keys
36
+
37
+ if options[:except]
38
+ hash_keys = hash_keys - Array(options[:except])
39
+ elsif options[:only]
40
+ hash_keys = hash_keys & Array(options[:only])
41
+ end
42
+
43
+ returning result = '{' do
44
+ result << hash_keys.map do |key|
45
+ "#{ActiveSupport::JSON.encode(key)}: #{ActiveSupport::JSON.encode(self[key], options)}"
46
+ end * ', '
47
+ result << '}'
48
+ end
49
+ end
50
+ end
@@ -0,0 +1,5 @@
1
+ class NilClass
2
+ def to_json(options = nil) #:nodoc:
3
+ 'null'
4
+ end
5
+ end
@@ -0,0 +1,5 @@
1
+ class Numeric
2
+ def to_json(options = nil) #:nodoc:
3
+ to_s
4
+ end
5
+ end
@@ -0,0 +1,6 @@
1
+ class Object
2
+ # Dumps object in JSON (JavaScript Object Notation). See www.json.org for more info.
3
+ def to_json(options = {})
4
+ ActiveSupport::JSON.encode(instance_values, options)
5
+ end
6
+ end
@@ -0,0 +1,5 @@
1
+ class Regexp
2
+ def to_json(options = nil) #:nodoc:
3
+ inspect
4
+ end
5
+ end
@@ -0,0 +1,30 @@
1
+ module ActiveSupport
2
+ module JSON
3
+ module Encoding
4
+ ESCAPED_CHARS = {
5
+ "\010" => '\b',
6
+ "\f" => '\f',
7
+ "\n" => '\n',
8
+ "\r" => '\r',
9
+ "\t" => '\t',
10
+ '"' => '\"',
11
+ '\\' => '\\\\',
12
+ '>' => '\u003E',
13
+ '<' => '\u003C',
14
+ '&' => '\u0026'
15
+ }
16
+ end
17
+ end
18
+ end
19
+
20
+ class String
21
+ def to_json(options = nil) #:nodoc:
22
+ '"' + gsub(/[\010\f\n\r\t"\\><&]/) { |s|
23
+ ActiveSupport::JSON::Encoding::ESCAPED_CHARS[s]
24
+ }.gsub(/([\xC0-\xDF][\x80-\xBF]|
25
+ [\xE0-\xEF][\x80-\xBF]{2}|
26
+ [\xF0-\xF7][\x80-\xBF]{3})+/nx) { |s|
27
+ s.unpack("U*").pack("n*").unpack("H*")[0].gsub(/.{4}/, '\\\\u\&')
28
+ } + '"'
29
+ end
30
+ end
@@ -0,0 +1,5 @@
1
+ class Symbol
2
+ def to_json(options = {}) #:nodoc:
3
+ ActiveSupport::JSON.encode(to_s, options)
4
+ end
5
+ end
@@ -0,0 +1,5 @@
1
+ class Time
2
+ def to_json(options = nil) #:nodoc:
3
+ to_datetime.to_json(options)
4
+ end
5
+ end
@@ -0,0 +1,5 @@
1
+ class TrueClass
2
+ def to_json(options = nil) #:nodoc:
3
+ 'true'
4
+ end
5
+ end
@@ -0,0 +1,38 @@
1
+ require 'active_support/json/variable'
2
+
3
+ require 'active_support/json/encoders/object' # Require explicitly for rdoc.
4
+ Dir["#{File.dirname(__FILE__)}/encoders/**/*.rb"].each do |file|
5
+ basename = File.basename(file, '.rb')
6
+ unless basename == 'object'
7
+ require "active_support/json/encoders/#{basename}"
8
+ end
9
+ end
10
+
11
+ module ActiveSupport
12
+ module JSON
13
+ class CircularReferenceError < StandardError
14
+ end
15
+
16
+ class << self
17
+ REFERENCE_STACK_VARIABLE = :json_reference_stack #:nodoc:
18
+
19
+ # Converts a Ruby object into a JSON string.
20
+ def encode(value, options = {})
21
+ raise_on_circular_reference(value) do
22
+ value.send(:to_json, options)
23
+ end
24
+ end
25
+
26
+ protected
27
+ def raise_on_circular_reference(value) #:nodoc:
28
+ stack = Thread.current[REFERENCE_STACK_VARIABLE] ||= []
29
+ raise CircularReferenceError, 'object references itself' if
30
+ stack.include? value
31
+ stack << value
32
+ yield
33
+ ensure
34
+ stack.pop
35
+ end
36
+ end
37
+ end
38
+ end
@@ -0,0 +1,10 @@
1
+ module ActiveSupport
2
+ module JSON
3
+ # A string that returns itself as its JSON-encoded form.
4
+ class Variable < String
5
+ def to_json(options=nil)
6
+ self
7
+ end
8
+ end
9
+ end
10
+ end
@@ -1,7 +1,9 @@
1
- module ActiveSupport::Multibyte #:nodoc:
2
- DEFAULT_NORMALIZATION_FORM = :kc
3
- NORMALIZATIONS_FORMS = [:c, :kc, :d, :kd]
4
- UNICODE_VERSION = '5.0.0'
1
+ module ActiveSupport
2
+ module Multibyte #:nodoc:
3
+ DEFAULT_NORMALIZATION_FORM = :kc
4
+ NORMALIZATIONS_FORMS = [:c, :kc, :d, :kd]
5
+ UNICODE_VERSION = '5.0.0'
6
+ end
5
7
  end
6
8
 
7
- require 'active_support/multibyte/chars'
9
+ require 'active_support/multibyte/chars'
@@ -41,6 +41,12 @@ module ActiveSupport::Multibyte #:nodoc:
41
41
  @string
42
42
  end
43
43
 
44
+ # Make duck-typing with String possible
45
+ def respond_to?(method)
46
+ super || @string.respond_to?(method) || handler.respond_to?(method) ||
47
+ (method.to_s =~ /(.*)!/ && handler.respond_to?($1)) || false
48
+ end
49
+
44
50
  # Create a new Chars instance.
45
51
  def initialize(str)
46
52
  @string = str.respond_to?(:string) ? str.string : str
@@ -140,6 +140,83 @@ module ActiveSupport::Multibyte::Handlers #:nodoc:
140
140
  bidx ? (u_unpack(str.slice(0...bidx)).size) : nil
141
141
  end
142
142
 
143
+ # Works just like the indexed replace method on string, except instead of byte offsets you specify
144
+ # character offsets.
145
+ #
146
+ # Example:
147
+ #
148
+ # s = "Müller"
149
+ # s.chars[2] = "e" # Replace character with offset 2
150
+ # s
151
+ # #=> "Müeler"
152
+ #
153
+ # s = "Müller"
154
+ # s.chars[1, 2] = "ö" # Replace 2 characters at character offset 1
155
+ # s
156
+ # #=> "Möler"
157
+ def []=(str, *args)
158
+ replace_by = args.pop
159
+ # Indexed replace with regular expressions already works
160
+ return str[*args] = replace_by if args.first.is_a?(Regexp)
161
+ result = u_unpack(str)
162
+ if args[0].is_a?(Fixnum)
163
+ raise IndexError, "index #{args[0]} out of string" if args[0] >= result.length
164
+ min = args[0]
165
+ max = args[1].nil? ? min : (min + args[1] - 1)
166
+ range = Range.new(min, max)
167
+ replace_by = [replace_by].pack('U') if replace_by.is_a?(Fixnum)
168
+ elsif args.first.is_a?(Range)
169
+ raise RangeError, "#{args[0]} out of range" if args[0].min >= result.length
170
+ range = args[0]
171
+ else
172
+ needle = args[0].to_s
173
+ min = index(str, needle)
174
+ max = min + length(needle) - 1
175
+ range = Range.new(min, max)
176
+ end
177
+ result[range] = u_unpack(replace_by)
178
+ str.replace(result.pack('U*'))
179
+ end
180
+
181
+ # Works just like String#rjust, only integer specifies characters instead of bytes.
182
+ #
183
+ # Example:
184
+ #
185
+ # "¾ cup".chars.rjust(8).to_s
186
+ # #=> " ¾ cup"
187
+ #
188
+ # "¾ cup".chars.rjust(8, " ").to_s # Use non-breaking whitespace
189
+ # #=> "   ¾ cup"
190
+ def rjust(str, integer, padstr=' ')
191
+ justify(str, integer, :right, padstr)
192
+ end
193
+
194
+ # Works just like String#ljust, only integer specifies characters instead of bytes.
195
+ #
196
+ # Example:
197
+ #
198
+ # "¾ cup".chars.rjust(8).to_s
199
+ # #=> "¾ cup "
200
+ #
201
+ # "¾ cup".chars.rjust(8, " ").to_s # Use non-breaking whitespace
202
+ # #=> "¾ cup   "
203
+ def ljust(str, integer, padstr=' ')
204
+ justify(str, integer, :left, padstr)
205
+ end
206
+
207
+ # Works just like String#center, only integer specifies characters instead of bytes.
208
+ #
209
+ # Example:
210
+ #
211
+ # "¾ cup".chars.center(8).to_s
212
+ # #=> " ¾ cup "
213
+ #
214
+ # "¾ cup".chars.center(8, " ").to_s # Use non-breaking whitespace
215
+ # #=> " ¾ cup  "
216
+ def center(str, integer, padstr=' ')
217
+ justify(str, integer, :center, padstr)
218
+ end
219
+
143
220
  # Does Unicode-aware rstrip
144
221
  def rstrip(str)
145
222
  str.gsub(UNICODE_TRAILERS_PAT, '')
@@ -169,11 +246,17 @@ module ActiveSupport::Multibyte::Handlers #:nodoc:
169
246
  # Implements Unicode-aware slice with codepoints. Slicing on one point returns the codepoints for that
170
247
  # character.
171
248
  def slice(str, *args)
172
- if (args.size == 2 && args.first.is_a?(Range))
173
- raise TypeError, 'cannot convert Range into Integer' # Do as if we were native
249
+ if args.size > 2
250
+ raise ArgumentError, "wrong number of arguments (#{args.size} for 1)" # Do as if we were native
251
+ elsif (args.size == 2 && !(args.first.is_a?(Numeric) || args.first.is_a?(Regexp)))
252
+ raise TypeError, "cannot convert #{args.first.class} into Integer" # Do as if we were native
253
+ elsif (args.size == 2 && !args[1].is_a?(Numeric))
254
+ raise TypeError, "cannot convert #{args[1].class} into Integer" # Do as if we were native
174
255
  elsif args[0].kind_of? Range
175
256
  cps = u_unpack(str).slice(*args)
176
257
  cps.nil? ? nil : cps.pack('U*')
258
+ elsif args[0].kind_of? Regexp
259
+ str.slice(*args)
177
260
  elsif args.size == 1 && args[0].kind_of?(Numeric)
178
261
  u_unpack(str)[args[0]]
179
262
  else
@@ -200,8 +283,8 @@ module ActiveSupport::Multibyte::Handlers #:nodoc:
200
283
  # Returns the KC normalization of the string by default. NFKC is considered the best normalization form for
201
284
  # passing strings to databases and validations.
202
285
  #
203
- # * <tt>str</tt>: The string to perform normalization on.
204
- # * <tt>form</tt>: The form you want to normalize in. Should be one of the following: :c, :kc, :d or :kd.
286
+ # * <tt>str</tt> - The string to perform normalization on.
287
+ # * <tt>form</tt> - The form you want to normalize in. Should be one of the following: :c, :kc, :d or :kd.
205
288
  def normalize(str, form=ActiveSupport::Multibyte::DEFAULT_NORMALIZATION_FORM)
206
289
  # See http://www.unicode.org/reports/tr15, Table 1
207
290
  codepoints = u_unpack(str)
@@ -235,8 +318,8 @@ module ActiveSupport::Multibyte::Handlers #:nodoc:
235
318
 
236
319
  # Used to translate an offset from bytes to characters, for instance one received from a regular expression match
237
320
  def translate_offset(str, byte_offset)
238
- return 0 if str == ''
239
321
  return nil if byte_offset.nil?
322
+ return 0 if str == ''
240
323
  chunk = str[0..byte_offset]
241
324
  begin
242
325
  begin
@@ -338,6 +421,33 @@ module ActiveSupport::Multibyte::Handlers #:nodoc:
338
421
  unpacked.flatten
339
422
  end
340
423
 
424
+ # Justifies a string in a certain way. Valid values for <tt>way</tt> are <tt>:right</tt>, <tt>:left</tt> and
425
+ # <tt>:center</tt>. Is primarily used as a helper method by <tt>rjust</tt>, <tt>ljust</tt> and <tt>center</tt>.
426
+ def justify(str, integer, way, padstr=' ')
427
+ raise ArgumentError, "zero width padding" if padstr.length == 0
428
+ padsize = integer - size(str)
429
+ padsize = padsize > 0 ? padsize : 0
430
+ case way
431
+ when :right
432
+ str.dup.insert(0, padding(padsize, padstr))
433
+ when :left
434
+ str.dup.insert(-1, padding(padsize, padstr))
435
+ when :center
436
+ lpad = padding((padsize / 2.0).floor, padstr)
437
+ rpad = padding((padsize / 2.0).ceil, padstr)
438
+ str.dup.insert(0, lpad).insert(-1, rpad)
439
+ end
440
+ end
441
+
442
+ # Generates a padding string of a certain size.
443
+ def padding(padsize, padstr=' ')
444
+ if padsize != 0
445
+ slice(padstr * ((padsize / size(padstr)) + 1), 0, padsize)
446
+ else
447
+ ''
448
+ end
449
+ end
450
+
341
451
  # Convert characters to a different case
342
452
  def to_case(way, str)
343
453
  u_unpack(str).map do |codepoint|