app/console/str.rb
the library's own source, console 0.2.0 · back to Console::Str
# Console::Str -- string helpers that avoid Regexp. # # mruby (the VM DragonRuby embeds) does not ship with the Regexp class, so # none of the standard regular-expression methods are available. Everything in # the console therefore uses these plain-string helpers instead. module Console module Str extend self # Remove a trailing suffix if present: chop('.rb', 'hello.rb') -> 'hello' def chop(str, suffix) s = str.to_s return s unless s.end_with?(suffix) s[0, s.length - suffix.length] end # Split on any of the characters in `chars`. # # String#split treats its argument as a literal separator, so passing # '-_' would split on that exact two-character sequence rather than on # either character. Splits per character instead. def split_on(str, chars) out = [''] str.to_s.each_char do |ch| if chars.include? ch out << '' else out[out.size - 1] += ch end end out end # Split into lines. def lines(str) str.to_s.split "\n" end # True when every character of `str` is a decimal digit and str is not # empty. Replaces the /\A\d+\z/ idiom. def digits?(str) s = str.to_s return false if s.length == 0 s.each_char do |ch| d = ch.ord - 48 return false if d < 0 || d > 9 end true end # Find the value of a quoted constant assignment, e.g. # title_of('TITLE = "Hello"') -> 'Hello'. # # Written with explicit length-based slicing rather than ranges, because # mruby's String#[] with a computed Range endpoint is not dependable. def quoted_value_after(str, prefix) s = str.to_s at = s.index prefix return nil if at.nil? after = after_at(s, at + prefix.length) eq = after.index '=' return nil if eq.nil? tail = after_at(after, eq + 1) # Pick whichever quote style appears first. dq = tail.index '"' sq = tail.index "'" quote_char = nil quote_at = nil if !dq.nil? && (sq.nil? || dq < sq) quote_char = '"' quote_at = dq elsif !sq.nil? quote_char = "'" quote_at = sq end return nil if quote_char.nil? body = after_at(tail, quote_at + 1) stop = body.index quote_char return nil if stop.nil? body[0, stop] end # s[n, s.length - n] without Range indexing. def after_at(s, n) return '' if n >= s.length s[n, s.length - n] end # Drop trailing slashes: chomp_slash('carts/space/') -> 'carts/space' def chomp_slash(str) s = str.to_s s = s[0, s.length - 1] while s.end_with?('/') && s.length > 1 s end # The last segment of a path: basename('carts/space') -> 'space' # # A cart's directory doubles as its name, so this is what turns a path back # into the module a cart is expected to define. def basename(str) chomp_slash(str).split('/').last.to_s end # Uppercase the first character: capitalize('hello') -> 'Hello' def capitalize(str) s = str.to_s return s if s.length == 0 s[0, 1].upcase + s[1, s.length] end # 'hello-world' -> 'HelloWorld' def camel(str) split_on(str, '-_').map { |p| capitalize p }.join end # 'HelloWorld' -> 'hello_world' def snake(str) s = str.to_s out = '' s.each_char do |ch| code = ch.ord if code >= 65 && code <= 90 out += '_' unless out.length == 0 out += ch.downcase else out += ch end end out end # Pad/truncate to exactly `width` characters. def fit(str, width) s = str.to_s return s[0, width] if s.length > width s end def blank?(str) str.nil? || str.to_s.length == 0 end end end