bioinform 0.1.11 → 0.1.12

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (27) hide show
  1. checksums.yaml +4 -4
  2. data/Gemfile +1 -1
  3. data/TODO.txt +2 -0
  4. data/bioinform.gemspec +0 -6
  5. data/lib/bioinform/data_models/motif.rb +1 -1
  6. data/lib/bioinform/data_models/pm.rb +2 -1
  7. data/lib/bioinform/data_models/pwm.rb +4 -5
  8. data/lib/bioinform/support/multiline_squish.rb +1 -1
  9. data/lib/bioinform/support/third_part/active_support/core_ext/array/extract_options.rb +29 -0
  10. data/lib/bioinform/support/third_part/active_support/core_ext/hash/indifferent_access.rb +23 -0
  11. data/lib/bioinform/support/third_part/active_support/core_ext/hash/keys.rb +54 -0
  12. data/lib/bioinform/support/third_part/active_support/core_ext/module/attribute_accessors.rb +64 -0
  13. data/lib/bioinform/support/third_part/active_support/core_ext/object/try.rb +57 -0
  14. data/lib/bioinform/support/third_part/active_support/core_ext/string/access.rb +99 -0
  15. data/lib/bioinform/support/third_part/active_support/core_ext/string/behavior.rb +6 -0
  16. data/lib/bioinform/support/third_part/active_support/core_ext/string/filters.rb +49 -0
  17. data/lib/bioinform/support/third_part/active_support/core_ext/string/multibyte.rb +72 -0
  18. data/lib/bioinform/support/third_part/active_support/hash_with_indifferent_access.rb +178 -0
  19. data/lib/bioinform/support/third_part/active_support/multibyte/chars.rb +476 -0
  20. data/lib/bioinform/support/third_part/active_support/multibyte/exceptions.rb +8 -0
  21. data/lib/bioinform/support/third_part/active_support/multibyte/unicode.rb +393 -0
  22. data/lib/bioinform/support/third_part/active_support/multibyte/utils.rb +60 -0
  23. data/lib/bioinform/support/third_part/active_support/multibyte.rb +44 -0
  24. data/lib/bioinform/support.rb +2 -2
  25. data/lib/bioinform/version.rb +1 -1
  26. data/spec/spec_helper.rb +10 -20
  27. metadata +21 -77
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 981be4ea2c5a07809303b9d4ac6a43f5d816e2f9
4
- data.tar.gz: 84aa6ce722f015b357c1ac87cbb0151c8f37a3d2
3
+ metadata.gz: bce1095da9311ea0d77479899064e74e5979c516
4
+ data.tar.gz: 3cdd8b774e7ad7cb930965bb5112c9046552f924
5
5
  SHA512:
6
- metadata.gz: 6b60eaa7651a2880354673d0e9f40e11407c76e76cb8ce526b60283049ca1e9e6dafdf16d258f77146987c507adfcc8d252485a38130278204015cbaaa31c4c3
7
- data.tar.gz: 45d806ad0a1befd381da35381f42d0cfcc1acd361bc35efa7ce9dacb203ab820e38564f8d93134ede8f5c380b1efc34ccc70ab1ee60ff7b3291c5ecc8d4e6c60
6
+ metadata.gz: 40c7a5191db1b2137de61d5e4b038fca9bb2ec14289ef3010a012a1fd1adb8f47d0b650bcd66b04ecfdac44104754a699ff8cf9b3e004e21834c8aeedddf521c
7
+ data.tar.gz: 9c2c7ce0367b030b318117f6fc7b597ed9886312e26c64f0bea32157485896f836cb7f30e91777b5f6df1944ac6cc2d53cf1b5c96019a236036fe306d4b86c9c
data/Gemfile CHANGED
@@ -13,4 +13,4 @@ group :development do
13
13
  gem 'fakefs', '~> 0.4.2'
14
14
  gem 'wdm', :require => false
15
15
  gem 'guard-rspec', '>=2.1.0'
16
- end
16
+ end
data/TODO.txt CHANGED
@@ -1,3 +1,5 @@
1
+ ! Make matrices immutable - it will allow more safe interface and better caching
2
+
1
3
  Collection contain Motif-s, each Motif can contain any of list: pcm,pwm,ppm.
2
4
  Name, background, tags and any other parameters should be removed from PM class to be placed in Motif
3
5
 
data/bioinform.gemspec CHANGED
@@ -15,11 +15,5 @@ Gem::Specification.new do |gem|
15
15
  gem.require_paths = ["lib"]
16
16
  gem.version = Bioinform::VERSION
17
17
 
18
- gem.add_dependency('activesupport', '>= 3.0.0')
19
18
  gem.add_dependency('docopt', '= 0.5.0')
20
-
21
- gem.add_development_dependency('fakefs', '~> 0.4.2')
22
- gem.add_development_dependency('fabrication', '~> 2.5.0')
23
- gem.add_development_dependency('rspec', '>= 2.0')
24
- gem.add_development_dependency('rspec-given', '>= 2.0.0')
25
19
  end
@@ -1,5 +1,5 @@
1
1
  require 'ostruct'
2
- require 'active_support/core_ext/object/try'
2
+ require_relative '../support/third_part/active_support/core_ext/object/try'
3
3
  require_relative '../support/parameters'
4
4
  module Bioinform
5
5
  class Motif
@@ -4,7 +4,8 @@ require_relative '../parsers'
4
4
  require_relative '../formatters'
5
5
 
6
6
  module Bioinform
7
- IndexByLetter = {'A' => 0, 'C' => 1, 'G' => 2, 'T' => 3, A: 0, C: 1, G: 2, T: 3}
7
+ IndexByLetter = { 'A' => 0, 'C' => 1, 'G' => 2, 'T' => 3, A: 0, C: 1, G: 2, T: 3,
8
+ 'a' => 0, 'c' => 1, 'g' => 2, 't' => 3, a: 0, c: 1, g: 2, t: 3}
8
9
  LetterByIndex = {0 => :A, 1 => :C, 2 => :G, 3 => :T}
9
10
 
10
11
  class PM
@@ -19,7 +19,6 @@ module Bioinform
19
19
  end
20
20
 
21
21
  def score(word)
22
- word = word.upcase
23
22
  raise ArgumentError, 'word in PWM#score(word) should have the same length as matrix' unless word.length == length
24
23
  #raise ArgumentError, 'word in PWM#score(word) should have only ACGT-letters' unless word.each_char.all?{|letter| %w{A C G T}.include? letter}
25
24
  (0...length).map do |pos|
@@ -27,11 +26,11 @@ module Bioinform
27
26
  if IndexByLetter[letter]
28
27
  matrix[pos][IndexByLetter[letter]]
29
28
  elsif letter == 'N'
30
- matrix[pos].zip(probability).map{|el, p| el * p}.inject(0.0, &:+)
29
+ matrix[pos].zip(probability).map{|el, p| el * p}.inject(0, &:+)
31
30
  else
32
31
  raise ArgumentError, "word in PWM#score(#{word}) should have only ACGT or N letters"
33
32
  end
34
- end.inject(0.0, &:+)
33
+ end.inject(0, &:+).to_f
35
34
  end
36
35
 
37
36
  def to_pwm
@@ -39,10 +38,10 @@ module Bioinform
39
38
  end
40
39
 
41
40
  def best_score
42
- @matrix.inject(0.0){|sum, col| sum + col.max}
41
+ best_suffix(0)
43
42
  end
44
43
  def worst_score
45
- @matrix.inject(0.0){|sum, col| sum + col.min}
44
+ worst_suffix(0)
46
45
  end
47
46
 
48
47
  # best score of suffix s[i..l]
@@ -1,4 +1,4 @@
1
- require 'active_support/core_ext/string/filters'
1
+ require_relative 'third_part/active_support/core_ext/string/filters'
2
2
  class String
3
3
  def multiline_squish
4
4
  split("\n").map(&:squish).join("\n").gsub(/\A\n+/,'').gsub(/\n+\z/,'')
@@ -0,0 +1,29 @@
1
+ class Hash
2
+ # By default, only instances of Hash itself are extractable.
3
+ # Subclasses of Hash may implement this method and return
4
+ # true to declare themselves as extractable. If a Hash
5
+ # is extractable, Array#extract_options! pops it from
6
+ # the Array when it is the last element of the Array.
7
+ def extractable_options?
8
+ instance_of?(Hash)
9
+ end
10
+ end
11
+
12
+ class Array
13
+ # Extracts options from a set of arguments. Removes and returns the last
14
+ # element in the array if it's a hash, otherwise returns a blank hash.
15
+ #
16
+ # def options(*args)
17
+ # args.extract_options!
18
+ # end
19
+ #
20
+ # options(1, 2) # => {}
21
+ # options(1, 2, :a => :b) # => {:a=>:b}
22
+ def extract_options!
23
+ if last.is_a?(Hash) && last.extractable_options?
24
+ pop
25
+ else
26
+ {}
27
+ end
28
+ end
29
+ end
@@ -0,0 +1,23 @@
1
+ require_relative '../../hash_with_indifferent_access'
2
+
3
+ class Hash
4
+ # Returns an <tt>ActiveSupport::HashWithIndifferentAccess</tt> out of its receiver:
5
+ #
6
+ # {:a => 1}.with_indifferent_access["a"] # => 1
7
+ #
8
+ def with_indifferent_access
9
+ ActiveSupport::HashWithIndifferentAccess.new_from_hash_copying_default(self)
10
+ end
11
+
12
+ # Called when object is nested under an object that receives
13
+ # #with_indifferent_access. This method will be called on the current object
14
+ # by the enclosing object and is aliased to #with_indifferent_access by
15
+ # default. Subclasses of Hash may overwrite this method to return +self+ if
16
+ # converting to an <tt>ActiveSupport::HashWithIndifferentAccess</tt> would not be
17
+ # desirable.
18
+ #
19
+ # b = {:b => 1}
20
+ # {:a => b}.with_indifferent_access["a"] # calls b.nested_under_indifferent_access
21
+ #
22
+ alias nested_under_indifferent_access with_indifferent_access
23
+ end
@@ -0,0 +1,54 @@
1
+ class Hash
2
+ # Return a new hash with all keys converted to strings.
3
+ #
4
+ # { :name => 'Rob', :years => '28' }.stringify_keys
5
+ # #=> { "name" => "Rob", "years" => "28" }
6
+ def stringify_keys
7
+ dup.stringify_keys!
8
+ end
9
+
10
+ # Destructively convert all keys to strings. Same as
11
+ # +stringify_keys+, but modifies +self+.
12
+ def stringify_keys!
13
+ keys.each do |key|
14
+ self[key.to_s] = delete(key)
15
+ end
16
+ self
17
+ end
18
+
19
+ # Return a new hash with all keys converted to symbols, as long as
20
+ # they respond to +to_sym+.
21
+ #
22
+ # { 'name' => 'Rob', 'years' => '28' }.symbolize_keys
23
+ # #=> { :name => "Rob", :years => "28" }
24
+ def symbolize_keys
25
+ dup.symbolize_keys!
26
+ end
27
+
28
+ # Destructively convert all keys to symbols, as long as they respond
29
+ # to +to_sym+. Same as +symbolize_keys+, but modifies +self+.
30
+ def symbolize_keys!
31
+ keys.each do |key|
32
+ self[(key.to_sym rescue key) || key] = delete(key)
33
+ end
34
+ self
35
+ end
36
+
37
+ alias_method :to_options, :symbolize_keys
38
+ alias_method :to_options!, :symbolize_keys!
39
+
40
+ # Validate all keys in a hash match *valid keys, raising ArgumentError on a mismatch.
41
+ # Note that keys are NOT treated indifferently, meaning if you use strings for keys but assert symbols
42
+ # as keys, this will fail.
43
+ #
44
+ # ==== Examples
45
+ # { :name => "Rob", :years => "28" }.assert_valid_keys(:name, :age) # => raises "ArgumentError: Unknown key: years"
46
+ # { :name => "Rob", :age => "28" }.assert_valid_keys("name", "age") # => raises "ArgumentError: Unknown key: name"
47
+ # { :name => "Rob", :age => "28" }.assert_valid_keys(:name, :age) # => passes, raises nothing
48
+ def assert_valid_keys(*valid_keys)
49
+ valid_keys.flatten!
50
+ each_key do |k|
51
+ raise(ArgumentError, "Unknown key: #{k}") unless valid_keys.include?(k)
52
+ end
53
+ end
54
+ end
@@ -0,0 +1,64 @@
1
+ require_relative '../array/extract_options'
2
+
3
+ class Module
4
+ def mattr_reader(*syms)
5
+ options = syms.extract_options!
6
+ syms.each do |sym|
7
+ class_eval(<<-EOS, __FILE__, __LINE__ + 1)
8
+ @@#{sym} = nil unless defined? @@#{sym}
9
+
10
+ def self.#{sym}
11
+ @@#{sym}
12
+ end
13
+ EOS
14
+
15
+ unless options[:instance_reader] == false || options[:instance_accessor] == false
16
+ class_eval(<<-EOS, __FILE__, __LINE__ + 1)
17
+ def #{sym}
18
+ @@#{sym}
19
+ end
20
+ EOS
21
+ end
22
+ end
23
+ end
24
+
25
+ def mattr_writer(*syms)
26
+ options = syms.extract_options!
27
+ syms.each do |sym|
28
+ class_eval(<<-EOS, __FILE__, __LINE__ + 1)
29
+ def self.#{sym}=(obj)
30
+ @@#{sym} = obj
31
+ end
32
+ EOS
33
+
34
+ unless options[:instance_writer] == false || options[:instance_accessor] == false
35
+ class_eval(<<-EOS, __FILE__, __LINE__ + 1)
36
+ def #{sym}=(obj)
37
+ @@#{sym} = obj
38
+ end
39
+ EOS
40
+ end
41
+ end
42
+ end
43
+
44
+ # Extends the module object with module and instance accessors for class attributes,
45
+ # just like the native attr* accessors for instance attributes.
46
+ #
47
+ # module AppConfiguration
48
+ # mattr_accessor :google_api_key
49
+ # self.google_api_key = "123456789"
50
+ #
51
+ # mattr_accessor :paypal_url
52
+ # self.paypal_url = "www.sandbox.paypal.com"
53
+ # end
54
+ #
55
+ # AppConfiguration.google_api_key = "overriding the api key!"
56
+ #
57
+ # To opt out of the instance writer method, pass :instance_writer => false.
58
+ # To opt out of the instance reader method, pass :instance_reader => false.
59
+ # To opt out of both instance methods, pass :instance_accessor => false.
60
+ def mattr_accessor(*syms)
61
+ mattr_reader(*syms)
62
+ mattr_writer(*syms)
63
+ end
64
+ end
@@ -0,0 +1,57 @@
1
+ class Object
2
+ # Invokes the method identified by the symbol +method+, passing it any arguments
3
+ # and/or the block specified, just like the regular Ruby <tt>Object#send</tt> does.
4
+ #
5
+ # *Unlike* that method however, a +NoMethodError+ exception will *not* be raised
6
+ # and +nil+ will be returned instead, if the receiving object is a +nil+ object or NilClass.
7
+ #
8
+ # If try is called without a method to call, it will yield any given block with the object.
9
+ #
10
+ # Please also note that +try+ is defined on +Object+, therefore it won't work with
11
+ # subclasses of +BasicObject+. For example, using try with +SimpleDelegator+ will
12
+ # delegate +try+ to target instead of calling it on delegator itself.
13
+ #
14
+ # ==== Examples
15
+ #
16
+ # Without +try+
17
+ # @person && @person.name
18
+ # or
19
+ # @person ? @person.name : nil
20
+ #
21
+ # With +try+
22
+ # @person.try(:name)
23
+ #
24
+ # +try+ also accepts arguments and/or a block, for the method it is trying
25
+ # Person.try(:find, 1)
26
+ # @people.try(:collect) {|p| p.name}
27
+ #
28
+ # Without a method argument try will yield to the block unless the receiver is nil.
29
+ # @person.try { |p| "#{p.first_name} #{p.last_name}" }
30
+ #--
31
+ # +try+ behaves like +Object#send+, unless called on +NilClass+.
32
+ def try(*a, &b)
33
+ if a.empty? && block_given?
34
+ yield self
35
+ else
36
+ __send__(*a, &b)
37
+ end
38
+ end
39
+ end
40
+
41
+ class NilClass
42
+ # Calling +try+ on +nil+ always returns +nil+.
43
+ # It becomes specially helpful when navigating through associations that may return +nil+.
44
+ #
45
+ # === Examples
46
+ #
47
+ # nil.try(:name) # => nil
48
+ #
49
+ # Without +try+
50
+ # @person && !@person.children.blank? && @person.children.first.name
51
+ #
52
+ # With +try+
53
+ # @person.try(:children).try(:first).try(:name)
54
+ def try(*args)
55
+ nil
56
+ end
57
+ end
@@ -0,0 +1,99 @@
1
+ require_relative "../../multibyte"
2
+
3
+ class String
4
+ unless '1.9'.respond_to?(:force_encoding)
5
+ # Returns the character at the +position+ treating the string as an array (where 0 is the first character).
6
+ #
7
+ # Examples:
8
+ # "hello".at(0) # => "h"
9
+ # "hello".at(4) # => "o"
10
+ # "hello".at(10) # => ERROR if < 1.9, nil in 1.9
11
+ def at(position)
12
+ mb_chars[position, 1].to_s
13
+ end
14
+
15
+ # Returns the remaining of the string from the +position+ treating the string as an array (where 0 is the first character).
16
+ #
17
+ # Examples:
18
+ # "hello".from(0) # => "hello"
19
+ # "hello".from(2) # => "llo"
20
+ # "hello".from(10) # => "" if < 1.9, nil in 1.9
21
+ def from(position)
22
+ mb_chars[position..-1].to_s
23
+ end
24
+
25
+ # Returns the beginning of the string up to the +position+ treating the string as an array (where 0 is the first character).
26
+ #
27
+ # Examples:
28
+ # "hello".to(0) # => "h"
29
+ # "hello".to(2) # => "hel"
30
+ # "hello".to(10) # => "hello"
31
+ def to(position)
32
+ mb_chars[0..position].to_s
33
+ end
34
+
35
+ # Returns the first character of the string or the first +limit+ characters.
36
+ #
37
+ # Examples:
38
+ # "hello".first # => "h"
39
+ # "hello".first(2) # => "he"
40
+ # "hello".first(10) # => "hello"
41
+ def first(limit = 1)
42
+ if limit == 0
43
+ ''
44
+ elsif limit >= size
45
+ self
46
+ else
47
+ mb_chars[0...limit].to_s
48
+ end
49
+ end
50
+
51
+ # Returns the last character of the string or the last +limit+ characters.
52
+ #
53
+ # Examples:
54
+ # "hello".last # => "o"
55
+ # "hello".last(2) # => "lo"
56
+ # "hello".last(10) # => "hello"
57
+ def last(limit = 1)
58
+ if limit == 0
59
+ ''
60
+ elsif limit >= size
61
+ self
62
+ else
63
+ mb_chars[(-limit)..-1].to_s
64
+ end
65
+ end
66
+ else
67
+ def at(position)
68
+ self[position]
69
+ end
70
+
71
+ def from(position)
72
+ self[position..-1]
73
+ end
74
+
75
+ def to(position)
76
+ self[0..position]
77
+ end
78
+
79
+ def first(limit = 1)
80
+ if limit == 0
81
+ ''
82
+ elsif limit >= size
83
+ self
84
+ else
85
+ to(limit - 1)
86
+ end
87
+ end
88
+
89
+ def last(limit = 1)
90
+ if limit == 0
91
+ ''
92
+ elsif limit >= size
93
+ self
94
+ else
95
+ from(-limit)
96
+ end
97
+ end
98
+ end
99
+ end
@@ -0,0 +1,6 @@
1
+ class String
2
+ # Enable more predictable duck-typing on String-like classes. See <tt>Object#acts_like?</tt>.
3
+ def acts_like_string?
4
+ true
5
+ end
6
+ end
@@ -0,0 +1,49 @@
1
+ require_relative 'multibyte'
2
+
3
+ class String
4
+ # Returns the string, first removing all whitespace on both ends of
5
+ # the string, and then changing remaining consecutive whitespace
6
+ # groups into one space each.
7
+ #
8
+ # Examples:
9
+ # %{ Multi-line
10
+ # string }.squish # => "Multi-line string"
11
+ # " foo bar \n \t boo".squish # => "foo bar boo"
12
+ def squish
13
+ dup.squish!
14
+ end
15
+
16
+ # Performs a destructive squish. See String#squish.
17
+ def squish!
18
+ strip!
19
+ gsub!(/\s+/, ' ')
20
+ self
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
+ # Pass a <tt>:separator</tt> to truncate +text+ at a natural break:
29
+ #
30
+ # "Once upon a time in a world far far away".truncate(27, :separator => ' ')
31
+ # # => "Once upon a time in a..."
32
+ #
33
+ # The last characters will be replaced with the <tt>:omission</tt> string (defaults to "...")
34
+ # for a total length not exceeding <tt>:length</tt>:
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
49
+ end
@@ -0,0 +1,72 @@
1
+ # encoding: utf-8
2
+ require_relative '../../multibyte'
3
+
4
+ class String
5
+ if RUBY_VERSION >= "1.9"
6
+ # == Multibyte proxy
7
+ #
8
+ # +mb_chars+ is a multibyte safe proxy for string methods.
9
+ #
10
+ # In Ruby 1.8 and older it creates and returns an instance of the ActiveSupport::Multibyte::Chars class which
11
+ # encapsulates the original string. A Unicode safe version of all the String methods are defined on this proxy
12
+ # class. If the proxy class doesn't respond to a certain method, it's forwarded to the encapsulated string.
13
+ #
14
+ # name = 'Claus Müller'
15
+ # name.reverse # => "rell??M sualC"
16
+ # name.length # => 13
17
+ #
18
+ # name.mb_chars.reverse.to_s # => "rellüM sualC"
19
+ # name.mb_chars.length # => 12
20
+ #
21
+ # In Ruby 1.9 and newer +mb_chars+ returns +self+ because String is (mostly) encoding aware. This means that
22
+ # it becomes easy to run one version of your code on multiple Ruby versions.
23
+ #
24
+ # == Method chaining
25
+ #
26
+ # All the methods on the Chars proxy which normally return a string will return a Chars object. This allows
27
+ # method chaining on the result of any of these methods.
28
+ #
29
+ # name.mb_chars.reverse.length # => 12
30
+ #
31
+ # == Interoperability and configuration
32
+ #
33
+ # The Chars object tries to be as interchangeable with String objects as possible: sorting and comparing between
34
+ # String and Char work like expected. The bang! methods change the internal string representation in the Chars
35
+ # object. Interoperability problems can be resolved easily with a +to_s+ call.
36
+ #
37
+ # For more information about the methods defined on the Chars proxy see ActiveSupport::Multibyte::Chars. For
38
+ # information about how to change the default Multibyte behavior see ActiveSupport::Multibyte.
39
+ def mb_chars
40
+ if ActiveSupport::Multibyte.proxy_class.consumes?(self)
41
+ ActiveSupport::Multibyte.proxy_class.new(self)
42
+ else
43
+ self
44
+ end
45
+ end
46
+
47
+ def is_utf8?
48
+ case encoding
49
+ when Encoding::UTF_8
50
+ valid_encoding?
51
+ when Encoding::ASCII_8BIT, Encoding::US_ASCII
52
+ dup.force_encoding(Encoding::UTF_8).valid_encoding?
53
+ else
54
+ false
55
+ end
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
71
+ end
72
+ end