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.
- data/CHANGELOG +263 -7
- data/lib/active_support.rb +9 -4
- data/lib/active_support/basic_object.rb +5 -0
- data/lib/active_support/buffered_logger.rb +107 -0
- data/lib/active_support/clean_logger.rb +94 -5
- data/lib/active_support/core_ext.rb +4 -1
- data/lib/active_support/core_ext/array.rb +8 -2
- data/lib/active_support/core_ext/array/access.rb +28 -0
- data/lib/active_support/core_ext/array/conversions.rb +28 -15
- data/lib/active_support/core_ext/array/extract_options.rb +19 -0
- data/lib/active_support/core_ext/array/grouping.rb +20 -7
- data/lib/active_support/core_ext/array/random_access.rb +12 -0
- data/lib/active_support/core_ext/bigdecimal.rb +1 -2
- data/lib/active_support/core_ext/bigdecimal/{formatting.rb → conversions.rb} +1 -2
- data/lib/active_support/core_ext/blank.rb +2 -8
- data/lib/active_support/core_ext/cgi.rb +2 -2
- data/lib/active_support/core_ext/class.rb +4 -3
- data/lib/active_support/core_ext/class/attribute_accessors.rb +1 -1
- data/lib/active_support/core_ext/class/delegating_attributes.rb +40 -0
- data/lib/active_support/core_ext/class/inheritable_attributes.rb +3 -3
- data/lib/active_support/core_ext/class/removal.rb +2 -2
- data/lib/active_support/core_ext/date.rb +5 -1
- data/lib/active_support/core_ext/date/behavior.rb +13 -0
- data/lib/active_support/core_ext/date/calculations.rb +188 -0
- data/lib/active_support/core_ext/date/conversions.rb +69 -13
- data/lib/active_support/core_ext/date_time.rb +10 -0
- data/lib/active_support/core_ext/date_time/calculations.rb +77 -0
- data/lib/active_support/core_ext/date_time/conversions.rb +54 -0
- data/lib/active_support/core_ext/duplicable.rb +37 -0
- data/lib/active_support/core_ext/enumerable.rb +1 -0
- data/lib/active_support/core_ext/exception.rb +2 -2
- data/lib/active_support/core_ext/file.rb +21 -0
- data/lib/active_support/core_ext/float.rb +5 -0
- data/lib/active_support/core_ext/float/rounding.rb +24 -0
- data/lib/active_support/core_ext/hash.rb +5 -5
- data/lib/active_support/core_ext/hash/conversions.rb +86 -34
- data/lib/active_support/core_ext/hash/diff.rb +8 -0
- data/lib/active_support/core_ext/hash/except.rb +24 -0
- data/lib/active_support/core_ext/hash/indifferent_access.rb +15 -2
- data/lib/active_support/core_ext/hash/keys.rb +10 -3
- data/lib/active_support/core_ext/hash/reverse_merge.rb +2 -2
- data/lib/active_support/core_ext/hash/slice.rb +28 -0
- data/lib/active_support/core_ext/integer.rb +2 -2
- data/lib/active_support/core_ext/kernel.rb +5 -4
- data/lib/active_support/core_ext/kernel/debugger.rb +13 -0
- data/lib/active_support/core_ext/module.rb +8 -7
- data/lib/active_support/core_ext/module/aliasing.rb +17 -5
- data/lib/active_support/core_ext/module/attr_accessor_with_default.rb +31 -0
- data/lib/active_support/core_ext/module/attribute_accessors.rb +1 -1
- data/lib/active_support/core_ext/module/delegation.rb +21 -0
- data/lib/active_support/core_ext/name_error.rb +2 -2
- data/lib/active_support/core_ext/numeric.rb +2 -2
- data/lib/active_support/core_ext/numeric/time.rb +30 -11
- data/lib/active_support/core_ext/object.rb +3 -2
- data/lib/active_support/core_ext/object/extending.rb +40 -29
- data/lib/active_support/core_ext/object/instance_variables.rb +22 -0
- data/lib/active_support/core_ext/object/misc.rb +29 -4
- data/lib/active_support/core_ext/pathname.rb +1 -1
- data/lib/active_support/core_ext/range.rb +7 -1
- data/lib/active_support/core_ext/range/blockless_step.rb +22 -0
- data/lib/active_support/core_ext/range/conversions.rb +8 -6
- data/lib/active_support/core_ext/range/include_range.rb +22 -0
- data/lib/active_support/core_ext/range/overlaps.rb +12 -0
- data/lib/active_support/core_ext/string.rb +10 -7
- data/lib/active_support/core_ext/string/conversions.rb +5 -1
- data/lib/active_support/core_ext/string/unicode.rb +2 -2
- data/lib/active_support/core_ext/string/xchar.rb +11 -0
- data/lib/active_support/core_ext/symbol.rb +12 -10
- data/lib/active_support/core_ext/test.rb +1 -0
- data/lib/active_support/core_ext/test/unit/assertions.rb +62 -0
- data/lib/active_support/core_ext/time.rb +4 -2
- data/lib/active_support/core_ext/time/behavior.rb +13 -0
- data/lib/active_support/core_ext/time/calculations.rb +87 -54
- data/lib/active_support/core_ext/time/conversions.rb +71 -10
- data/lib/active_support/dependencies.rb +25 -24
- data/lib/active_support/deprecation.rb +4 -2
- data/lib/active_support/duration.rb +86 -0
- data/lib/active_support/inflections.rb +2 -1
- data/lib/active_support/inflector.rb +13 -6
- data/lib/active_support/json.rb +22 -39
- data/lib/active_support/json/decoding.rb +60 -0
- data/lib/active_support/json/encoders/date.rb +5 -0
- data/lib/active_support/json/encoders/date_time.rb +5 -0
- data/lib/active_support/json/encoders/enumerable.rb +12 -0
- data/lib/active_support/json/encoders/false_class.rb +5 -0
- data/lib/active_support/json/encoders/hash.rb +50 -0
- data/lib/active_support/json/encoders/nil_class.rb +5 -0
- data/lib/active_support/json/encoders/numeric.rb +5 -0
- data/lib/active_support/json/encoders/object.rb +6 -0
- data/lib/active_support/json/encoders/regexp.rb +5 -0
- data/lib/active_support/json/encoders/string.rb +30 -0
- data/lib/active_support/json/encoders/symbol.rb +5 -0
- data/lib/active_support/json/encoders/time.rb +5 -0
- data/lib/active_support/json/encoders/true_class.rb +5 -0
- data/lib/active_support/json/encoding.rb +38 -0
- data/lib/active_support/json/variable.rb +10 -0
- data/lib/active_support/multibyte.rb +7 -5
- data/lib/active_support/multibyte/chars.rb +6 -0
- data/lib/active_support/multibyte/handlers/utf8_handler.rb +115 -5
- data/lib/active_support/option_merger.rb +7 -7
- data/lib/active_support/ordered_options.rb +22 -17
- data/lib/active_support/test_case.rb +5 -0
- data/lib/active_support/testing.rb +1 -0
- data/lib/active_support/testing/default.rb +12 -0
- data/lib/active_support/values/time_zone.rb +3 -3
- data/lib/active_support/vendor.rb +14 -0
- data/lib/active_support/vendor/builder-2.1.2/blankslate.rb +113 -0
- data/lib/active_support/vendor/{builder.rb → builder-2.1.2/builder.rb} +0 -0
- data/lib/active_support/vendor/builder-2.1.2/builder/blankslate.rb +20 -0
- data/lib/active_support/vendor/builder-2.1.2/builder/css.rb +250 -0
- data/lib/active_support/vendor/{builder → builder-2.1.2/builder}/xchar.rb +11 -8
- data/lib/active_support/vendor/{builder → builder-2.1.2/builder}/xmlbase.rb +38 -44
- data/lib/active_support/vendor/{builder → builder-2.1.2/builder}/xmlevents.rb +1 -1
- data/lib/active_support/vendor/{builder → builder-2.1.2/builder}/xmlmarkup.rb +40 -39
- data/lib/active_support/vendor/{xml_simple.rb → xml-simple-1.0.11/xmlsimple.rb} +3 -3
- data/lib/active_support/version.rb +3 -3
- data/lib/active_support/whiny_nil.rb +12 -12
- data/lib/activesupport.rb +1 -0
- metadata +69 -17
- data/lib/active_support/binding_of_caller.rb +0 -84
- data/lib/active_support/breakpoint.rb +0 -528
- data/lib/active_support/caching_tools.rb +0 -62
- data/lib/active_support/json/encoders.rb +0 -25
- data/lib/active_support/json/encoders/core.rb +0 -70
- data/lib/active_support/reloadable.rb +0 -60
- 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,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,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,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,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
|
@@ -1,7 +1,9 @@
|
|
1
|
-
module ActiveSupport
|
2
|
-
|
3
|
-
|
4
|
-
|
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
|
173
|
-
raise
|
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
|
204
|
-
# * <tt>form</tt
|
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|
|