Ruby Sugar Refinery


The Ruby Sugar Refinery is a collection of tiny refinements (declarative local core extensions) for Ruby.

Setup & Usage

Add to Gemfile:

gem 'sugar_refinery', require: 'sugar_refinery/all'

Then add refinements to the current Ruby file or module:

using SugarRefinery::CamelSnake
using SugarRefinery::HashZip

"ClassName".to_snake # => "class_name"
Hash.zip [1,2,3], [4,5,6] # => {1=>4, 2=>5, 3=>6}

List of Refinements

alias_for

Summary A different way to create aliases: Reversed order and you can pass multiple alias names. The order feels more logical
Activate
using SugarRefinery::AliasFor
Usage
Module#alias_for, Module#aliases_for
# creates an alias for the method :methods with the name ms
class Object
  alias_for :methods, :ms
end

# creates multiple aliases
module Enumerable
  aliases_for :zip, :with, :%
end
Specification (show)
require 'sugar_refinery/alias_for'
using SugarRefinery::AliasFor


describe 'alias_for' do
  it 'should create an alias for instance methods' do
    class Array
      def m2
        2
      end
      aliases_for :m2, :a2, :'a-2'
    end
    proc do
      [1,2,3].a2.should == 2
      [1,2,3].send('a-2').should == 2
    end.should_not raise_exception
  end

  it 'should create an alias for class (singleton) methods' do
    class Array
      class << Array
        def m3
          3
        end
        alias_for :m3, :a3
      end
    end

    proc{
      Array.a3.should == 3
    }.should_not raise_exception
  end


  it 'should create aliases for the first argument with all other arguments' do
    class Object
      def m4
        4
      end
      alias_for :m4, :ma, :mb, :mc
    end

    proc{
      1.ma.should   == 4
      "1".mb.should == 4
      [1].mc.should == 4
    }.should_not raise_exception
  end

  it 'works with uncommon chars' do
    class Object
      alias_for :tainted?, :"tain-ted?"
    end
  end
end
Source (show)
require_relative 'version'

module SugarRefinery
  module AliasFor
    refine Module do
      private

      def alias_for(m, *aliases)
        aliases.each{ |a| class_eval "alias :'#{ a }' :'#{ m }'" }
      end

      alias aliases_for alias_for
    end
  end
end

array_op

Summary Operators one could miss for Array
Activate
using SugarRefinery::ArrayOp
Usage
Array#^
[1,2,3,4] ^ [3,4,5,6]  # => [1,2,5,6]
Specification (show)
require 'sugar_refinery/array_op'
using SugarRefinery::ArrayOp


describe 'Array#^' do
  it 'does an exclusive or' do
    a = [1,2,3,4]
    b = [3,4,5,6]
    (a^b).should == [1,2,5,6]
  end
end

describe 'Array#**' do
  it 'returns the array product' do
    ([1,2] ** %w[a b]).should == [[1, "a"], [1, "b"], [2, "a"], [2, "b"]]
  end
end
Source (show)
require_relative 'version'

module SugarRefinery
  module ArrayOp
    refine Array do
      def ^(other)
        (self - other) + (other - self)
      end

      def **(*o, &block)
        product *o, &block
      end
    end
  end
end

array_stats

Summary Statistics for Array
Activate
using SugarRefinery::ArrayStats
Usage
Array#mean
# calculates mean (average)
Array#stdev
# calculates standard deviation (takes :sample or :population as argument)
Array#stdev_sample
# calculates sample standard deviation
Array#stdev_population
# calculates population standard deviation
Array#z_score
# calculates standard score
Specification (show)
require "sugar_refinery/array_stats"
using SugarRefinery::ArrayStats

describe "doing statistics on arrays" do
  let(:list) { (2..5).to_a }

  describe "Array#mean" do
    it "should return the average" do
      list.mean.should == 3.5
    end
  end

  describe "Array#stdev_sample" do
    it "should return the standard deviation of the sample" do
      list.stdev_sample.should be_close(1.2909944487358056, 1e-8)
    end
  end

  describe "Array#stdev_population" do
    it "should return the standard deviation of the population" do
      list.stdev_population.should be_close(1.118033988749895, 1e-8)
    end
  end

  describe "Array#stdev" do
    it "should default to population" do
      list.stdev.should be_close(list.stdev_population, 1e-8)
    end

    it "should delegate sample correctly" do
      list.stdev(:sample).should be_close(list.stdev_sample, 1e-8)
    end

    it "should delegate population correctly" do
      list.stdev(:population).should be_close(list.stdev_population, 1e-8)
    end

    it "should raise an error with any other key" do
      expect { list.stdev(:pony) }.to raise_error(ArgumentError)
    end
  end

  describe "Array#z_score" do
    it "should default to population" do
      list.z_score.zip(list.z_score(:population)).each do |value, actual|
        value.should be_close(actual, 1e-8)
      end
    end

    it "should delegate sample correctly" do
      sample_z_score = [
        -1.161895003862225,
        -0.3872983346207417,
        0.3872983346207417,
        1.161895003862225
      ]

      p list

      list.z_score(:sample).zip(sample_z_score).each do |value, actual|
        value.should be_close(actual, 1e-8)
      end
    end

    it "should delegate population correctly" do
      population_z_score = [
        -1.3416407864998738,
        -0.4472135954999579,
        0.4472135954999579,
        1.3416407864998738
      ]

      list.z_score(:population).zip(population_z_score).each do |value, actual|
        value.should be_close(actual, 1e-8)
      end
    end

    it "should raise an error with any other key" do
      expect { list.z_score(:pony) }.to raise_error(ArgumentError)
    end
  end
end
Source (show)
require_relative 'version'

module SugarRefinery
  module ArrayStats
    refine Array do
      def mean
        inject(&:+) / length.to_f
      end

      def stdev(type = :population)
        case type
        when :population then stdev_population
        when :sample     then stdev_sample
        else raise ArgumentError.new("%s is not a valid argument" % type)
        end
      end

      def stdev_sample
        Math.sqrt(inject(0.0) { |sum, x| sum + (mean - x) ** 2 } / (length - 1))
      end

      def stdev_population
        Math.sqrt(inject(0.0) { |sum, x| sum + (mean - x) ** 2 } / length)
      end

      def z_score(type = :population)
        map { |x| (x - mean) / stdev(type) }
      end
    end
  end
end

blank

Summary Does pretty the same as in ActiveSupport: Every object can be asked if it is blank or present
Activate
using SugarRefinery::Blank
Usage
Object#blank?
'an object'.blank? # => false
nil.present? # => false
Specification (show)
require 'sugar_refinery/blank'
using SugarRefinery::Blank


describe 'Object#blank?' do
  it 'should be blank for blank values' do
    blank_values   = [ nil, false, '', '   ', "  \n\t  \r ", [], {}, // ]

    blank_values.each{ |blank|
      blank.blank?.should == true
    }
  end

  it 'should not be blank for non blank values' do
    present_values = [ Object.new, true, 0, 1, 'a', [nil], { nil => nil } ]

    present_values.each{ |present|
      present.blank?.should == false
    }
  end
end
Source (show)
require_relative 'version'

module SugarRefinery
  module Blank
    refine Object do
      def blank?
        if respond_to? :empty? then empty? else !self end
      end

      def present?
        !blank?
      end
    end

    refine NilClass do
      def blank?() true end
    end

    refine FalseClass do
      def blank?() true end
    end

    refine TrueClass do
      def blank?() false end
    end

    refine Numeric do
      def blank?() false end
    end

    refine Array do
      def blank?() empty? end
    end

    refine Hash do
      def blank?() empty? end
    end

    refine String do
      def blank?() self !~ /\S/ end
    end

    refine Regexp do
      def blank?() self == // end
    end
  end
end

camel_snake

Summary Provides String#to_camel and String#to_snake.
Activate
using SugarRefinery::CamelSnake
Usage
String#to_camel
'was_snake_string'.to_camel # => 'WasSnakeString'
String#to_snake
'WasCamelString'.to_snake # => 'was_camel_string'
Specification (show)
require 'sugar_refinery/camel_snake'
using SugarRefinery::CamelSnake


describe 'String#to_camel' do
  it 'should turn a snake_cased string to CamelCase' do
    'was_snake_case'.to_camel.should == 'WasSnakeCase'
  end
end

describe 'String#to_snake' do
  it 'should turn a CamelCased string to snake_case' do
    'WasCamelCase'.to_snake.should == 'was_camel_case'
  end
end
Source (show)
require_relative 'version'

module SugarRefinery
  module CamelSnake
    refine String do
      def to_snake
        gsub(/(?<!^)[A-Z]/) do "_#$&" end.downcase
      end

      def to_camel
        gsub(/(?:^|_)([a-z])/) do $1.upcase end
      end
    end
  end
end

chain_map

Summary Chain multiple procs on Array elements
Activate
using SugarRefinery::ChainMap
Usage
Array#chain_map
[-3, -2, -1].chain_map(:abs, :to_s)  # => ["3", "2", "1"]
Specification (show)
require "sugar_refinery/chain_map"
using SugarRefinery::ChainMap

describe "Array#chain_map" do
  it "should repeatedly chain symbols as map calls" do
    list = [-3, -2, -1]

    list.chain_map(:abs, :to_s) == %w{3 2 1}
  end

  it "should repeatedly chain blocks as map calls" do
    list = [-3, -2, -1]

    list.chain_map(->(e) { e ** 2 }, ->(e) { e * -1 }) == [-9, -4, -1]
  end
end
Source (show)
require_relative 'version'

module SugarRefinery
  module ChainMap
    refine Array do
      def chain_map(*args)
        args.inject(self) { |collection, action| collection.map(&action) }
      end
    end
  end
end

constantize

Summary Easier handling of dynamic constant names
Activate
using SugarRefinery::Constantize
Usage
String#constantize
'Object'.constantize # => Object
'Spec::VERSION'.constantize # => Spec::VERSION if rspec is loaded
# you can also pass a parameter or block to define what happens, when constant does not exist
'IdontExist'.constantize(Array) # => Array
'String5'.constantize do |string|
  string.chop.constantize
end # => String
Information An improved version of ActiveSupport's one
Specification (show)
require 'sugar_refinery/constantize'
using SugarRefinery::Constantize


describe 'String#constantize' do
  it 'should return the constant with that name' do
    'Object'.constantize.should == Object
  end

  it 'should also work for nested constants' do
    'SugarRefinery::VERSION'.constantize.should == SugarRefinery::VERSION
  end

  it 'should throw name error if constant does not exist (and no parameter is given)' do
    proc do
      'ObfsefsefsefafesafaefRubySugarRefineryafdfselijfesject'.constantize
    end.should raise_exception NameError
  end

  it 'should call the block (and not raise an error) if constant does not exist and block given' do
    proc do
      'ObfsefsefsefafesafaefRubySugarRefineryafdfselijfesject'.constantize do |string|
         Default = [1,2,3]
      end.should == [1,2,3]
    end.should_not raise_exception
  end

  it 'should return the second parameter (and not raise an error) if constant does not exist and parameter given' do
    proc do
      'ObfsefsefsefafesafaefRubySugarRefineryafdfselijfesject'.constantize(Array).should == Array
    end.should_not raise_exception
  end
end
Source (show)
require_relative 'version'

module SugarRefinery
  module Constantize
    refine String do
      def constantize(default_value = nil) # always uses global scope as in AS... is this good?
        get_constant = lambda{
          self.split(/::/).inject( Object ){ |base_constant, current_constant|
            base_constant.const_get current_constant
          }
        }

        if !default_value && !block_given?
          get_constant.call
        else
          begin
            get_constant.call
          rescue NameError
            if block_given?
              yield self
            else
              default_value
            end
          end
        end
      end
    end
  end
end

dir_utils

Summary Some shortcuts for working with directories
Activate
using SugarRefinery::DirUtils
Usage
Dir.join
File.join
Dir.split
File.split
Dir.rm
FileUtils.rm_r
Specification (show)
require 'sugar_refinery/dir_utils'
require 'securerandom'
using SugarRefinery::DirUtils


describe Dir do
  describe '.join' do
    it 'delegates to File.join' do
      a = %w[some file path]
      expect( Dir.join(a) ).to be == File.join(a)
    end
  end

  describe '.split' do
    it 'delegates to File.split' do
      a = 'some/file/path'
      expect( Dir.split(a) ).to be == File.split(a)
    end
  end

  describe '.rm' do
    it 'removes directories with content' do
      path = "tmp_#{SecureRandom.uuid}"
      FileUtils.mkdir path
      FileUtils.touch "#{path}/123"
      expect{
        Dir.rm path
      }.not_to raise_error
      expect( Dir['*'] ).to_not include(path)
    end
  end
end
Source (show)
require_relative 'version'
require 'fileutils'

module SugarRefinery
  module DirUtils
    refine Dir.singleton_class do
      def join(*args)
        File.join(*args)
      end

      def split(*args)
        File.split(*args)
      end

      def rm(*args)
        FileUtils.rm_r(*args)
      end
    end
  end
end

file_force_delete

Summary File.delete! (like `rm -f`)
Activate
using SugarRefinery::FileForceDelete
Usage
File.delete!
File.delete! 'some_filename' # will delete the file, don't care if it exist.
Information Idea for File.delete! from sugar-high.
Specification (show)
require 'sugar_refinery/file_force_delete'
require 'fileutils'
using SugarRefinery::FileForceDelete


describe File do
  describe '.delete!' do
    after{ File.delete! 'random_filename' }
    let :random_filename do
      'test_' + (0..20).map{|e| [*'a'..'z'].sample }*''
    end

    it 'should delete the filename given as argument if it exists + return non-nil' do
      FileUtils.touch random_filename
      res = false
      proc do
        res = File.delete! random_filename
      end.should_not raise_exception
      res.should be_truthy
    end

    it 'should do nothing if the filename given as argument does not exist + return nil' do
      res = false
      proc do
        res = File.delete! random_filename
      end.should_not raise_exception
      res.should be_falsey
    end
  end
end
Source (show)
require_relative 'version'

module SugarRefinery
  module FileForceDelete
    refine File.singleton_class do
      def delete!(filename)
        return nil if !File.exist?(filename)
        File.delete filename
      end
    end
  end
end

file_gsub

Summary Will read the file and substitute the hash keys with their values
Activate
using SugarRefinery::FileGsub
Usage
File.gsub
File.gsub 'some_filename',
  /hi/ => 'cu',
  /\d/ => proc{ |m| (m.to_i+1).to_s }
Specification (show)
require 'sugar_refinery/file_gsub'
require 'fileutils'
using SugarRefinery::FileGsub


describe File do
  describe '.gsub' do
    let :random_filename do
      'test_' + (0..20).map{|e| [*'a'..'z'].sample }*''
    end

    it 'should read filename in arg1, substitute every key in the arg2 with its value and save the file' do
      File.open(random_filename,'w'){ |file|
        file.print 'should read filename in arg1, substitute every key in the arg2 with its value and save the file'
      }
      File.gsub random_filename,
        /read/ => 'write',
        /\d+/  => proc{|m| (m.to_i+1).to_s }

      File.read(random_filename).should ==
        'should write filename in arg2, substitute every key in the arg3 with its value and save the file'

      FileUtils.rm random_filename
    end
  end
end
Source (show)
require_relative 'version'

module SugarRefinery
  module FileGsub
    refine File.singleton_class do
      def gsub(filename, regex_hash)
        data = File.read filename
        File.open(filename,'w'){ |file|
          regex_hash.each{ |regex, new_string|
            regex = regex.to_s unless regex.is_a? Regexp

            if new_string.is_a? Proc
              data.gsub! regex, &new_string
            else
              data.gsub! regex, new_string
            end
          }
          file.print data
        }
      end
    end
  end
end

hash_op

Summary Sugar for dealing with hashes
Activate
using SugarRefinery::HashOp
Usage
Hash#<<
{1=>2} << [3, 4]   # => { 1=>2, 3=>4 }
{1=>2} << { 5=>6 } # => { 1=>2, 5=>6 }
Hash#&
{ 1=>4, 2=>5, 3=>6 } & { 1=>4, 2=>7 } # => { 1=>4 }
Information Some of the operators are inspired by Ruby Facets.
Specification (show)
require 'sugar_refinery/hash_op'
using SugarRefinery::HashOp


describe 'Hash#<<' do
  it 'appends new elements to the hash' do
    a =  { 1=>4, 2=>5, 3=>6 }
    a << { 4=>7 }
    a << [5, 8]
    a.should == { 1=>4, 2=>5, 3=>6, 4=>7, 5=>8 }
  end
end

describe 'Hash#&' do
  it 'selects a sub hash containt only equal key-value pairs' do
    a = { 1=>4, 2=>5, 3=>6 }
    b = { 1=>4, 2=>7 }
    (a & b).should == { 1=>4 }
  end
end

describe 'Hash#+' do
  it 'merges two hashes' do
    a = { 1=>4, 2=>5, 3=>6 }
    b = { 1=>4, 2=>7, 4=>0 }
    (a + b).should == { 1=>4, 2=>7, 3=>6, 4=>0 }
  end
end
Source (show)
require_relative 'version'

module SugarRefinery
  module HashOp
    refine Hash do
      def <<(other)
        case
        when other.is_a?(Hash)
          merge! other
        when other.is_a?(Enumerable) || other.respond_to?(:to_splat)
          merge! Hash[*other]
        else
          raise TypeError, 'can only append other Hashs and Enumerables (or Classes that implement to_splat)'
        end
      end

      def &(other)
        Hash[ *select{ |k,v|
          other[k] == v
        }.flatten ]
      end

      def +(*o, &block)
        merge *o, &block
      end
    end
  end
end

hash_zip

Summary Adds a Hash.zip method, which makes sense.
Activate
using SugarRefinery::HashZip
Usage
Hash.zip
Hash.zip [1,2,3], [4,5,6] # => {1=>4, 2=>5, 3=>6}
Specification (show)
require 'sugar_refinery/hash_zip'
using SugarRefinery::HashZip


describe Hash do
  describe '.zip' do
    it 'should zip together both given enumerables and take them as key=>values for a new hash' do
      Hash.zip( [1,2,3], [4,5,6] ).should == { 1=>4, 2=>5, 3=>6 }
    end
  end
end
Source (show)
require_relative 'version'

module SugarRefinery
  module HashZip
    refine Hash.singleton_class do
      def zip(keys, values)
        Hash[ *keys.zip(values).flatten ]
      end
    end
  end
end

inner_map

Summary Runs blocks on inner Array elements
Activate
using SugarRefinery::InnerMap
Usage
Array#inner_map
[[1, 2], [3, 4]].inner_map{ |i| i + 1 }  # => [[2, 3], [4, 5]]
Array#inner_inject
[[1, 2], [3, 4]].inner_inject(&:+)  # => [3, 7]
Specification (show)
require "sugar_refinery/inner_map"
using SugarRefinery::InnerMap

describe "Array#inner_map" do
  it "should delegate map to inner lists" do
    list = [[1, 2], [3, 4]]

    list.inner_map { |i| i + 1 }.should == [[2, 3], [4, 5]]
  end
end

describe "Array#inner_inject" do
  it "should delegate inject to inner lists" do
    list = [%w{a b c}, %w{d e f}]

    list.inner_inject(&:+).should == list.map(&:join)
  end

  it "should take default values" do
    list = [[3, 2, 1], [-4]]

    list.inner_inject(4, &:+).should == [10, 0]
  end
end
Source (show)
require_relative 'version'

module SugarRefinery
  module InnerMap
    refine Array do
      def inner_map(&block)
        map { |object| object.map(&block) }
      end

      def inner_inject(default = :not_used, &block)
        map { |object| default == :not_used ? object.inject(&block) : object.inject(default, &block) }
      end
    end
  end
end

lchomp

Summary Adds String#chomp on the left side.
Activate
using SugarRefinery::Lchomp
Usage
String#lchomp
'  Yes'.lchomp  # => ' Yes'
String#lchomp!
# mutable lchomp version
Specification (show)
require 'sugar_refinery/lchomp'
using SugarRefinery::Lchomp

describe String do
  describe '#lchomp' do
    it 'should chomp on the left side' do
      string = 'ameise'
      expect( string.lchomp('a') ).to eq 'meise'
      expect( string ).to eq 'ameise'
    end
  end

  describe '#lchomp!' do
    it 'should chomp on the left side (mutating)' do
      string = 'ameise'
      expect( string.lchomp!('a') ).to eq 'meise'
      expect( string ).to eq 'meise'
    end
  end
end
Source (show)
require_relative 'version'

module SugarRefinery
  module Lchomp
    refine String do
      def lchomp(arg = $/)
        reverse.chomp(arg).reverse
      end

      def lchomp!(arg = $/)
        replace reverse.chomp(arg).reverse
      end
    end
  end
end

marshal_copy

Summary Adds Object#marshal_copy to create a deep copy using Marshal.
Activate
using SugarRefinery::MarshalCopy
Usage
Object#marshal_copy
a = %w[hello world]
b = a.marshal_copy
Specification (show)
require 'sugar_refinery/marshal_copy'
using SugarRefinery::MarshalCopy


describe 'Object#marshal_copy' do
  it 'create a (deep) copy via marshalling' do
    a = %w[hello world]
    b = a.marshal_copy
    b.should == a

    b[0][1,1] = ''
    b.should_not == a
  end
end
Source (show)
require_relative 'version'

module SugarRefinery
  module MarshalCopy
    refine Object do
      def marshal_copy
        Marshal.load Marshal.dump self
      end
    end
  end
end

mash

Summary mash: map(hash)
Activate
using SugarRefinery::Mash
Usage
Enumerable#mash
[1,2,3].mash{|e| [e, e.to_s] } # => {1=>'1',2=>'2',3=>'3'}
Information Inspired by Ruby Facets' mash.
Specification (show)
require 'sugar_refinery/mash'
using SugarRefinery::Mash


describe 'Array#mash' do
  it 'should "map" a hash' do
    [1,2,3].mash{|e| [e, e.to_s] }.should == {1=>'1',2=>'2',3=>'3',}
  end
end


describe 'Enumerator#mash' do
  it 'should "map" a hash' do
    [1,2,3].each.mash{|e| [e, e.to_s] }.should == {1=>'1',2=>'2',3=>'3',}
  end
end
Source (show)
require_relative 'version'

module SugarRefinery
  module Mash
    refine Enumerator do
      def mash
        ret = {}
        each{ |kv| ret.store( *(yield(kv)[0,2]) ) }
        ret
      end
    end

    refine Array do
      def mash
        ret = {}
        each{ |kv| ret.store( *(yield(kv)[0,2]) ) }
        ret
      end
    end
  end
end

regexp_union

Summary Easy creation of a Regexp.union
Activate
using SugarRefinery::RegexpUnion
Usage
Regexp#|, String#|
/Ruby\d/ | /test/i | "cheat"
# creates a Regexp similar to:
# /(Ruby\d|[tT][eE][sS][tT]|cheat)/
Specification (show)
require 'sugar_refinery/regexp_union'
using SugarRefinery::RegexpUnion


shared_examples_for "Regexp.union operator" do
  it "creates a Regexp.union of both operands" do
    (/Ruby\d/ | /test/i | "cheat").should ==
      Regexp.union( Regexp.union( /Ruby\d/, /test/i ), "cheat" )
  end
end

describe 'Regexp#|' do
  it_should_behave_like 'Regexp.union operator'
end

describe 'String#|' do
  it_should_behave_like 'Regexp.union operator'
end
Source (show)
require_relative 'version'

module SugarRefinery
  module RegexpUnion
    refine Regexp do
      def |(arg)
        Regexp.union self, arg.is_a?(Regexp) ? arg : arg.to_s
      end
    end

    refine String do
      def |(arg)
        Regexp.union self, arg.is_a?(Regexp) ? arg : arg.to_s
      end
    end
  end
end

same

Summary Adds Float#same? for correct number comparisons.
Activate
using SugarRefinery::Same
Usage
Float#same?
(0.1 + 0.2).same? 0.3 # true
Specification (show)
require 'sugar_refinery/same'
using SugarRefinery::Same

describe Float do
  describe '#same?' do
    it 'returns true if other float represents the same number' do
      expect( 0.3.same?(0.1 + 0.2) ).to be true
    end
  end
end
Source (show)
require_relative 'version'

module SugarRefinery
  module Same
    refine Float do
      def same?(other)
        (self - other).abs < Float::EPSILON
      end
    end
  end
end

string_op

Summary More String operators. Because Strings cannot be comfortable enough!
Activate
using SugarRefinery::StringOp
Usage
String#-
'1234abc5678' - 'b' - /\d/ # => ac
String#^
'Yes vs No'^2 # => 's vs No'
Specification (show)
require 'sugar_refinery/string_op'
using SugarRefinery::StringOp


describe 'String#-' do
  it 'should remove the applied Regexp or String from self via gsub' do
    ('1234abc5678' - 'b' - /\d/).should == 'ac'
  end
end

describe 'String#^' do
  it 'should give C-like substring access to strings' do
    string = 'Theoretische Informatik ist voll geil!'

    (string^0).should  == 'Theoretische Informatik ist voll geil!'
    (string^1).should  == 'heoretische Informatik ist voll geil!'
    (string^13).should == 'Informatik ist voll geil!'
    (string^-1).should == 'Theoretische Informatik ist voll geil'
    (string^38).should == ''
    (string^99).should == nil
  end
end
Source (show)
require_relative 'version'

module SugarRefinery
  module StringOp
    refine String do
      def -(rem)
        gsub( Regexp === rem ? rem : rem.to_s, '' )
      end

      def ^(pos)
        pos = pos.to_i
        if pos >= 0
          self[pos..-1]
        else
          self[0...pos]
        end
      end
    end
  end
end

Changelog

# SUGAR REFINERY CHANGELOG

## 2016-01-03 | Sugar Refinery 1.0.0

* turn zucker gem into sugar_refinery
* move iterate into its own gem
* move instance_variable_from into its own gem
* move egonil into its own gem
* remove 'Zucker#require' and 'Zucker#require_all'
* remove 'refine' gem dependency
* remove unary_conversion in favor of https://bugs.ruby-lang.org/issues/11782
* remove square_brackets_for
* remove tap
* rename string_extras to lchomp
* rename hash_extras to hash_zip
* rename float_extras to same
* rename dir_extras to dir_utils
* split file_extras into file_force_delete and file_gsub (also remove File.filename)


## OLD ZUCKER CHANGELOG

2015-04-01 | Zucker 100.1.0
* drop Ruby 1.9 support
* add float_extras for Float#same?
* add chain_map
* add inner_map
* add array_stats


2014-04-01 | Zucker 100.0.0
* drop Ruby 1.8 support
* rewrite zucker to use refinements!
* use usual semantic versioning
* remove all "debug" cubes, use the "debugging" gem instead!
* remove "ruby_version" cube, use the "ruby_version" gem instead!
* remove "engine" cube, use the "ruby_engine" gem instead!
* remove "info" cube, use the "ruby_info" gem instead!
* remove "os" cube, use rdp's "os" gem instead!
* remove "*_to_proc" cubes, use the "procstar" gem instead!
* remove "sandbox" cube, since sandboxing is not recommended and removed from Ruby 2.1
* remove "kernel" and "not" cube completely
* tweaks to most other cubes


2013-04-30 | Zucker 13
* fix alias_for bug
* let RubyVersion#inspect and RubyEngine#inspect map to #to_s
* remove Array#sum
* repackage (gh#4)


2012-01-16 | Zucker 12
* fix a requiring bug
* rename zucker/version cube to zucker/ruby_version and Zucker::PACKAGES to Zucker::PACKS
* remove more_aliases! (keep it simple...)
* add case cube: String#to_camel and String#to_snake


2011-05-25 | Zucker 11
* add Zucker::constants to check if cube has been required (e.g. Zucker::AliasFor if 'zucker/alias_for' is loaded)
* rbx tweaks
  * don't define Binding#vars on rbx (prevent endless recursion)
  * sandbox-not-working warning
* add Kernel#ignore_sigquit!
* fix warnings
* doc tweaks


2011-04-29 | Zucker 10
* doc/spec tweaks
  * make test.rubygems.org-testable
* fix zucker 9 permission issue


2011-01-22 | Zucker 9
* remove history versions (sorry, it caused too much gem/rdoc troubles)
* add file cube: File.delete! and File.gsub
* debug pack improvements
  * binding: typos + return nil
  * cc: support for ripl + return nil
  * mm: also show eigenclass for modules + nicer displaying + return nil
  * added Regexp#visualize
* remove optional sandbox param
* rename xxx2proc to xxx_to_proc
* change rakefile/rspec/gemspec structure
* more minor fixes and improvements


2010-10-06 | Zucker 8
* fix a little alias_for bug
* disable rdoc creation when installing (in favour of the custom docs)
* change Binding#inspect to Binding.variables (was too verbose and dangerous)


2010-10-03 | Zucker 7
* fix critical OS.windows? bug


2010-10-03 | Zucker 6
* no new cubes
* bugfix for OS.posix?
* small changes + bugfixes + doc improvements
* add two user methods to Info
* change egonil semantics ( using method_missing, see http://ruby.janlelis.de/26/catch_nil.rb )
* bugfix for vv


2010-09-04 | Zucker 5
* debug edition -  add two debug helpers: oo (output line, method, file) and cc (output method callstack)
* rename cube D to dd add add more debug aliases (for mm and binding)
* fix __SPECIAL_VARS__ in info and kernel cube and some minor bugfixes
* Zucker.activate_more_aliases! option
* add Hash#&
* add aliases: File.filename (for basename), Dir.join and Dir.split (for File.join, split)
* add a flexible requiring mechansim in zucker.rb (no api changes)
* restructure packages
* add rake tasks for releasing
* improve RubyVersion constant (cleaner and more flexible)


2010-09-01 | Zucker 4
* fix Binding#inspect
* add RubyEngine constant
* add RubyVersion constant
* add OS constant
* add q debug method (like p but on one line)
* add String#-


2010-08-14 | Zucker 3
* add tap cube
* add Object#not
* add alias_for for an alternative alias syntax
* add String#constantize (improved AS version)
* improve Info module
* make Array#sum Rails compatibile
* improve docs
* change directory layout (no changes for requiring)
* more small changes


2010-08-08 | Zucker 2
* add info cube
* add chaining for array2proc
* fix Hash.zip
* fix instance_variables_from binding for 1.9
* more specs


2010-08-06 | Zucker 1
* initial release


2010-08-05 | Zucker 0
* pre-release for rug-b talk


Fork me on GitHub