forked from mikel/mail
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Pulling Active Support multibyte requirements over for Ruby 1.8.7
- Loading branch information
Mikel Lindsaar
committed
Apr 26, 2011
1 parent
3c4c2c8
commit 6eb4c44
Showing
17 changed files
with
1,336 additions
and
81 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,98 @@ | ||
|
||
class String | ||
unless '1.9'.respond_to?(:force_encoding) | ||
# Returns the character at the +position+ treating the string as an array (where 0 is the first character). | ||
# | ||
# Examples: | ||
# "hello".at(0) # => "h" | ||
# "hello".at(4) # => "o" | ||
# "hello".at(10) # => ERROR if < 1.9, nil in 1.9 | ||
def at(position) | ||
mb_chars[position, 1].to_s | ||
end | ||
|
||
# Returns the remaining of the string from the +position+ treating the string as an array (where 0 is the first character). | ||
# | ||
# Examples: | ||
# "hello".from(0) # => "hello" | ||
# "hello".from(2) # => "llo" | ||
# "hello".from(10) # => "" if < 1.9, nil in 1.9 | ||
def from(position) | ||
mb_chars[position..-1].to_s | ||
end | ||
|
||
# Returns the beginning of the string up to the +position+ treating the string as an array (where 0 is the first character). | ||
# | ||
# Examples: | ||
# "hello".to(0) # => "h" | ||
# "hello".to(2) # => "hel" | ||
# "hello".to(10) # => "hello" | ||
def to(position) | ||
mb_chars[0..position].to_s | ||
end | ||
|
||
# Returns the first character of the string or the first +limit+ characters. | ||
# | ||
# Examples: | ||
# "hello".first # => "h" | ||
# "hello".first(2) # => "he" | ||
# "hello".first(10) # => "hello" | ||
def first(limit = 1) | ||
if limit == 0 | ||
'' | ||
elsif limit >= size | ||
self | ||
else | ||
mb_chars[0...limit].to_s | ||
end | ||
end | ||
|
||
# Returns the last character of the string or the last +limit+ characters. | ||
# | ||
# Examples: | ||
# "hello".last # => "o" | ||
# "hello".last(2) # => "lo" | ||
# "hello".last(10) # => "hello" | ||
def last(limit = 1) | ||
if limit == 0 | ||
'' | ||
elsif limit >= size | ||
self | ||
else | ||
mb_chars[(-limit)..-1].to_s | ||
end | ||
end | ||
else | ||
def at(position) | ||
self[position] | ||
end | ||
|
||
def from(position) | ||
self[position..-1] | ||
end | ||
|
||
def to(position) | ||
self[0..position] | ||
end | ||
|
||
def first(limit = 1) | ||
if limit == 0 | ||
'' | ||
elsif limit >= size | ||
self | ||
else | ||
to(limit - 1) | ||
end | ||
end | ||
|
||
def last(limit = 1) | ||
if limit == 0 | ||
'' | ||
elsif limit >= size | ||
self | ||
else | ||
from(-limit) | ||
end | ||
end | ||
end | ||
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,72 @@ | ||
# encoding: utf-8 | ||
require 'mail/multibyte' | ||
|
||
class String | ||
if RUBY_VERSION >= "1.9" | ||
# == Multibyte proxy | ||
# | ||
# +mb_chars+ is a multibyte safe proxy for string methods. | ||
# | ||
# In Ruby 1.8 and older it creates and returns an instance of the Mail::Multibyte::Chars class which | ||
# encapsulates the original string. A Unicode safe version of all the String methods are defined on this proxy | ||
# class. If the proxy class doesn't respond to a certain method, it's forwarded to the encapsuled string. | ||
# | ||
# name = 'Claus Müller' | ||
# name.reverse # => "rell??M sualC" | ||
# name.length # => 13 | ||
# | ||
# name.mb_chars.reverse.to_s # => "rellüM sualC" | ||
# name.mb_chars.length # => 12 | ||
# | ||
# In Ruby 1.9 and newer +mb_chars+ returns +self+ because String is (mostly) encoding aware. This means that | ||
# it becomes easy to run one version of your code on multiple Ruby versions. | ||
# | ||
# == Method chaining | ||
# | ||
# All the methods on the Chars proxy which normally return a string will return a Chars object. This allows | ||
# method chaining on the result of any of these methods. | ||
# | ||
# name.mb_chars.reverse.length # => 12 | ||
# | ||
# == Interoperability and configuration | ||
# | ||
# The Chars object tries to be as interchangeable with String objects as possible: sorting and comparing between | ||
# String and Char work like expected. The bang! methods change the internal string representation in the Chars | ||
# object. Interoperability problems can be resolved easily with a +to_s+ call. | ||
# | ||
# For more information about the methods defined on the Chars proxy see Mail::Multibyte::Chars. For | ||
# information about how to change the default Multibyte behaviour see Mail::Multibyte. | ||
def mb_chars | ||
if Mail::Multibyte.proxy_class.consumes?(self) | ||
Mail::Multibyte.proxy_class.new(self) | ||
else | ||
self | ||
end | ||
end | ||
|
||
def is_utf8? #:nodoc | ||
case encoding | ||
when Encoding::UTF_8 | ||
valid_encoding? | ||
when Encoding::ASCII_8BIT, Encoding::US_ASCII | ||
dup.force_encoding(Encoding::UTF_8).valid_encoding? | ||
else | ||
false | ||
end | ||
end | ||
else | ||
def mb_chars | ||
if Mail::Multibyte.proxy_class.wants?(self) | ||
Mail::Multibyte.proxy_class.new(self) | ||
else | ||
self | ||
end | ||
end | ||
|
||
# Returns true if the string has UTF-8 semantics (a String used for purely byte resources is unlikely to have | ||
# them), returns false otherwise. | ||
def is_utf8? | ||
Mail::Multibyte::Chars.consumes?(self) | ||
end | ||
end | ||
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,26 +1,142 @@ | ||
module Mail | ||
# Sort of like ActiveSupport HashWithIndifferentAccess, but lighter | ||
class IndifferentHash < Hash | ||
def initialize(other=nil) | ||
if other.is_a?(Hash) | ||
self.default = other.default | ||
self.update(other) | ||
|
||
def initialize(constructor = {}) | ||
if constructor.is_a?(Hash) | ||
super() | ||
update(constructor) | ||
else | ||
super(constructor) | ||
end | ||
end | ||
|
||
def default(key = nil) | ||
if key.is_a?(Symbol) && include?(key = key.to_s) | ||
self[key] | ||
else | ||
super | ||
end | ||
end | ||
|
||
def [](key_name) | ||
super(key_name.to_sym) | ||
def self.new_from_hash_copying_default(hash) | ||
IndifferentHash.new(hash).tap do |new_hash| | ||
new_hash.default = hash.default | ||
end | ||
end | ||
|
||
def []=(k, v) | ||
super(k.to_sym, v) | ||
alias_method :regular_writer, :[]= unless method_defined?(:regular_writer) | ||
alias_method :regular_update, :update unless method_defined?(:regular_update) | ||
|
||
# Assigns a new value to the hash: | ||
# | ||
# hash = HashWithIndifferentAccess.new | ||
# hash[:key] = "value" | ||
# | ||
def []=(key, value) | ||
regular_writer(convert_key(key), convert_value(value)) | ||
end | ||
|
||
alias_method :store, :[]= | ||
|
||
# Updates the instantized hash with values from the second: | ||
# | ||
# hash_1 = HashWithIndifferentAccess.new | ||
# hash_1[:key] = "value" | ||
# | ||
# hash_2 = HashWithIndifferentAccess.new | ||
# hash_2[:key] = "New Value!" | ||
# | ||
# hash_1.update(hash_2) # => {"key"=>"New Value!"} | ||
# | ||
def update(other_hash) | ||
super(other_hash.inject({}) {|c, (k, v)| c[k.to_sym] = v; c}) | ||
other_hash.each_pair { |key, value| regular_writer(convert_key(key), convert_value(value)) } | ||
self | ||
end | ||
alias merge! update | ||
|
||
alias_method :merge!, :update | ||
|
||
# Checks the hash for a key matching the argument passed in: | ||
# | ||
# hash = HashWithIndifferentAccess.new | ||
# hash["key"] = "value" | ||
# hash.key? :key # => true | ||
# hash.key? "key" # => true | ||
# | ||
def key?(key) | ||
super(convert_key(key)) | ||
end | ||
|
||
alias_method :include?, :key? | ||
alias_method :has_key?, :key? | ||
alias_method :member?, :key? | ||
|
||
# Fetches the value for the specified key, same as doing hash[key] | ||
def fetch(key, *extras) | ||
super(convert_key(key), *extras) | ||
end | ||
|
||
# Returns an array of the values at the specified indices: | ||
# | ||
# hash = HashWithIndifferentAccess.new | ||
# hash[:a] = "x" | ||
# hash[:b] = "y" | ||
# hash.values_at("a", "b") # => ["x", "y"] | ||
# | ||
def values_at(*indices) | ||
indices.collect {|key| self[convert_key(key)]} | ||
end | ||
|
||
# Returns an exact copy of the hash. | ||
def dup | ||
IndifferentHash.new(self) | ||
end | ||
|
||
# Merges the instantized and the specified hashes together, giving precedence to the values from the second hash | ||
# Does not overwrite the existing hash. | ||
def merge(hash) | ||
self.dup.update(hash) | ||
end | ||
|
||
# Performs the opposite of merge, with the keys and values from the first hash taking precedence over the second. | ||
# This overloaded definition prevents returning a regular hash, if reverse_merge is called on a HashWithDifferentAccess. | ||
def reverse_merge(other_hash) | ||
super self.class.new_from_hash_copying_default(other_hash) | ||
end | ||
|
||
def reverse_merge!(other_hash) | ||
replace(reverse_merge( other_hash )) | ||
end | ||
|
||
# Removes a specified key from the hash. | ||
def delete(key) | ||
super(convert_key(key)) | ||
end | ||
|
||
def stringify_keys!; self end | ||
def stringify_keys; dup end | ||
def symbolize_keys; to_hash.symbolize_keys end | ||
def to_options!; self end | ||
|
||
def to_hash | ||
Hash.new(default).merge!(self) | ||
end | ||
|
||
protected | ||
|
||
def convert_key(key) | ||
key.kind_of?(Symbol) ? key.to_s : key | ||
end | ||
|
||
def convert_value(value) | ||
if value.class == Hash | ||
self.class.new_from_hash_copying_default(value) | ||
elsif value.is_a?(Array) | ||
value.dup.replace(value.map { |e| convert_value(e) }) | ||
else | ||
value | ||
end | ||
end | ||
|
||
end | ||
end |
Oops, something went wrong.