Ruby Zucker


Zucker adds syntactic sugar in the form of independent, small scripts that sweeten Ruby even more. It is completely based on Ruby's refinements feature. You can actively choose for each source file, which features to include. If your Ruby version does not support refinements, plain old core extensions will be used. The source code is available at github.

Install

$ gem install zucker Or in your Gemfile: gem 'zucker', require: 'zucker/all'

Usage / Organization

require 'zucker/all'

Choose which refinements (sugar cubes) you want to use in this Ruby file:

using Zucker::CamelSnake
using Zucker::HashExtras

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

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 Zucker::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 'zucker/alias_for'
using Zucker::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 'zucker'

module Zucker
  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 Zucker::ArrayOp
Usage
Array#^
[1,2,3,4] ^ [3,4,5,6]  # => [1,2,5,6]
Specification (show)
require 'zucker/array_op'
using Zucker::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 'zucker'

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

      def **(*o, &block)
        product *o, &block
      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 Zucker::Blank
Usage
Object#blank?
'an object'.blank? # => false
nil.present? # => false
Specification (show)
require 'zucker/blank'
using Zucker::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 'zucker'

module Zucker
  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 Zucker::CamelSnake
Usage
String#to_camel
'was_snake_string'.to_camel # => 'WasSnakeString'
String#to_snake
'WasCamelString'.to_snake # => 'was_camel_string'
Specification (show)
require 'zucker/camel_snake'
using Zucker::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 'zucker'

module Zucker
  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

constantize

Summary Easier handling of dynamic constant names
Activate
using Zucker::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 'zucker/constantize'
using Zucker::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
    'Zucker::VERSION'.constantize.should == Zucker::VERSION
  end

  it 'should throw name error if constant does not exist (and no parameter is given)' do
    proc do
      'ObfsefsefsefafesafaefRubyZuckerafdfselijfesject'.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
      'ObfsefsefsefafesafaefRubyZuckerafdfselijfesject'.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
      'ObfsefsefsefafesafaefRubyZuckerafdfselijfesject'.constantize(Array).should == Array
    end.should_not raise_exception
  end
end
Source (show)
require 'zucker'

module Zucker
  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_extras

Summary Some shortcuts for working with directories
Activate
using Zucker::DirExtras
Usage
Dir.join
File.join
Dir.split
File.split
Dir.rm
FileUtils.rm_r
Specification (show)
require 'zucker/dir_extras'
require 'securerandom'
using Zucker::DirExtras


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 'zucker'
require 'fileutils'

module Zucker
  module DirExtras
    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

egonil

Summary Creates a block, where nil does not raise NoMethodErrors
Activate
using Zucker::Egonil
Usage
egonil
egonil{
  nil.some_methods.that[:do].not.exist
}
Information See this post for more information and discussion.
Specification (show)
require 'zucker/egonil'
using Zucker::Egonil

describe 'egonil' do
  it 'should not raise nil exceptions in the block' do
    proc do
      egonil{ nil.some_methods.that[:do].not.exist }
    end.should_not raise_exception
  end

  it 'should restore default behaviour after the block' do
    proc do
      egonil{ nil.some_methods.that[:do].not.exist }
    end.should_not raise_exception

    proc do
      nil.a_method
    end.should raise_exception NoMethodError
  end

  it 'raise NoMethodError for non-nil objects' do
    proc do
      egonil{ 5.a_method }
    end.should raise_exception NoMethodError
  end

  it 'should raise other Exceptions' do
    proc do
      egonil{ 5 / 0 }
    end.should raise_exception ZeroDivisionError
  end
end
Source (show)
require 'zucker'

module Zucker
  module Egonil
    refine Object do
      private

      def egonil(&block)
        mm = NilClass.instance_method(:method_missing)
        NilClass.send :define_method, :method_missing, ->(*){}
        yield
      ensure
        NilClass.send :define_method, :method_missing, mm
      end
    end
  end
end

file

Summary Extensions for File
Activate
using Zucker::File
Usage
File.gsub
File.gsub 'some_filename', # will read the file and substitute the hash keys with their values
  /hi/ => 'cu',
  /\d/ => proc{ |m| (m.to_i+1).to_s }
File.delete!
File.delete! 'some_filename' # will delete the file, don't care if it exist.
File.filename
File.basename
Information Idea for File.delete! from sugar-high.
Specification (show)
FIXME: missing (please create a github issue)
Source (show)
FIXME: missing (please create a github issue)

hash_extras

Summary Sugar for dealing with hashes
Activate
using Zucker::HashExtras
Usage
Hash.zip
Hash.zip [1,2,3], [4,5,6] # => {1=>4, 2=>5, 3=>6}
Specification (show)
require 'zucker/hash_extras'
using Zucker::HashExtras


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 'zucker'

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

hash_op

Summary Sugar for dealing with hashes
Activate
using Zucker::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 'zucker/hash_op'
using Zucker::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 'zucker'

module Zucker
  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

instance_variables_from

Summary Reduces boilerplate code assigning lots of instance variables:

def initialize(variable1, variable2)
  @variable1, @variable2 = variable1, variable2
end

Activate
using Zucker::InstanceVariablesFrom
Usage
instance_variables_from
def some_method(a = 1, b = 2)
  instance_variables_from binding # assigns @a and @b

  params = {:c => 3, :d => 4}
  instance_variables_from params # # assigns @c and @d
end
Specification (show)
require 'zucker/instance_variables_from'
using Zucker::InstanceVariablesFrom


describe 'instance_variables_from' do
  it 'transforms the given parameter to instance variables when in it is a binding' do
    def example_method(a = 1, b = 2)
      instance_variables_from binding # assigns @a and @b
    end

    example_method
    @a.should == 1
    @b.should == 2
  end

  it 'transforms the given parameter to instance variables when in it is a hash' do
    params = { c: 3, d: 4 }
    instance_variables_from params

    @c.should == 3
    @d.should == 4
  end
end
Source (show)
require 'zucker'

module Zucker
  module InstanceVariablesFrom
    refine Object do
      private

      def instance_variables_from(obj, *only)
        iter =
          if    obj.is_a? Binding
            obj.eval('local_variables').map{|e| [obj.eval("#{e}"), e] }
          elsif obj.is_a? Hash
            obj.map{|k,v| [v,k] }
          else
        #  elsif obj.is_a? Enumerable
            obj.each.with_index
          end

        ret = []
        iter.each{ |value, arg|
          arg = arg.to_s
          if only.include?(arg) || only.include?(arg.to_sym) || only.empty?
            arg = '_' + arg  if (48..57).member? arg.unpack('C')[0]  # 1.8+1.9
            ret << ivar = :"@#{arg}"
            self.instance_variable_set ivar, value
          end
        }
        ret
      end
    end
  end
end

iterate

Summary Iterate over one or more collections. It is like .each with two differences: It feels more like a control structure and you can easily iterate over multiple objects
Activate
using Zucker::Iterate
Usage
iterate
iterate [1,2], [3,4,5] do |e,f|
  puts "#{e},#{f}"
end
# outputs
#  1,3
#  2,4
#  ,5
Specification (show)
require 'zucker/iterate'
using Zucker::Iterate


describe 'Object#iterate' do
  let :a   do [1, 2, 3]     end
  let :b   do %w|a b c d|   end
  let :res do Hash.new {[]} end

  it 'should behave like Enumerable#each for a single argument' do
    iterate a do |ele|
      res[:iter] << ele
    end

    a.each do |ele|
      res[:each] << ele
    end

    res[:iter].should == res[:each]
  end

  it 'should pass the right params to the block' do
    res = Hash.new {[]} # TODO: why?
    res[:iter_a_b] = [] # ....
    res[:iter_b_a] = [] # ....


    iterate a, b do |e,f|
      res[:iter_a_b] << [e, f]
    end

    res[:iter_a_b].should == [
      [1, 'a'],
      [2, 'b'],
      [3, 'c'],
      [nil, 'd'],
    ]

    iterate b, a do |e,f|
      res[:iter_b_a] << [e, f]
    end

    res[:iter_b_a].should == [
      ['a', 1],
      ['b', 2],
      ['c', 3],
      ['d', nil],
    ]

  end

  it 'should return enumerators if no block is applied' do
    res = Hash.new {[]} # TODO: why?
    res[:iter_a_b] = [] # ....
    res[:iter_b_a] = [] # ....

    enumerator = iterate a,b
    enumerator.should be_kind_of(RUBY_VERSION < '1.9' ? Enumerable::Enumerator : Enumerator)
    enumerator.to_a.should == [[1,'a'], [2,'b'], [3,'c'], [nil, 'd']]
  end
end
Source (show)
require 'zucker'

module Zucker
  module Iterate
    refine Object do
      def iterate(*params)
        # params.shift.zip(*params).each{ |*elements| yield *elements }
        raise ArgumentError, "wrong number of arguments (0)" if params.empty?

        first = params.shift
        if params.empty? # single param - like each
          if block_given?
            first.map{|e| yield e }
          else
            first.map.to_enum
          end
        else # multiple params
          max_size = [first, *params].max_by(&:count).size
          padded_first = first.to_a + [nil]*(max_size - first.count)  # append nils
          obj = padded_first.zip(*params)
          if block_given?
            obj.map{|es| yield(*es) }
          else
            obj.map.to_enum
          end
        end
      end
    end
  end
end

marshal_copy

Summary Adds Object#marshal_copy to create a deep copy using Marshal.
Activate
using Zucker::MarshalCopy
Usage
Object#marshal_copy
a = %w[hello world]
b = a.mcopy
Specification (show)
require 'zucker/marshal_copy'
using Zucker::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 'zucker'

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

mash

Summary mash: map(hash)
Activate
using Zucker::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 'zucker/mash'
using Zucker::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 'zucker'

module Zucker
  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 Zucker::RegexpUnion
Usage
Regexp#|, String#|
/Ruby\d/ | /test/i | "cheat"
# creates a Regexp similar to:
# /(Ruby\d|[tT][eE][sS][tT]|cheat)/
Specification (show)
require 'zucker/regexp_union'
using Zucker::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 'zucker'

module Zucker
  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

square_brackets_for

Summary This helper methods defines [] and []= for accesing an instance variable
Activate
using Zucker::SquareBracketsFor
Usage
square_brackets_for
class Klass
  def initialize
    @var = {
      :a_key => 1,
      :another_one => 2,
    }
  end

  square_brackets_for :var # creates [] and []=
  # square_brackets_for :var, false # would create only []
end

a = Klass.new
a[:a_key] # => 1
Specification (show)
require 'zucker/square_brackets_for'
using Zucker::SquareBracketsFor


describe 'square_brackets_for' do
  before do
    class Klass
      def initialize
        @var = { a_key: 1, another_one: 2 }
      end

      @eigenvar = { a_key: 99 }
    end
  end

  it 'should define a [] getter (not a setter) for an instance var, if the second parameter is false' do
    class Klass
      square_brackets_for :var, nil
    end

    a = Klass.new
    a[:a_key].should == 1

    proc do
      a[:this_is] = 'not possible'
    end.should raise_exception NoMethodError
  end

  it 'should define [] and []= for accessing an instance variable' do
    class Klass
      square_brackets_for :var
    end

    a = Klass.new
    a[:a_key].should == 1

    a[:this_is] = 'useful'
    a[:this_is].should == 'useful'
  end

  it 'should also work for class-instance variables' do
    class Klass
      class << Klass
        square_brackets_for :eigenvar
      end
    end

    Klass[:a_key].should == 99
  end
end
Source (show)
require 'zucker'

module Zucker
  module SquareBracketsFor
    refine Object do
      def square_brackets_for(ivar, assignment = true)
        define_method :[] do |key|
          (instance_variable_get :"@#{ivar}")[key]
        end

        if assignment
          define_method :[]= do |key, value|
            (instance_variable_get :"@#{ivar}")[key] = value
          end
        end
      end
    end
  end
end

string_extras

Summary Generic String extensions. Strings cannot be comfortable enough!
Activate
using Zucker::StringExtras
Usage
String#lchomp
'  Yes'.lchomp  # => ' Yes'
String#lchomp!
# mutable lchomp version
Specification (show)
require 'zucker/string_extras'
using Zucker::StringExtras

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 'zucker'

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

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

string_op

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

module Zucker
  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

tap

Summary Adds the two tap variants tap_on (formerly known as returning in ActiveSupport) and make_new
Activate
using Zucker::Tap
Usage
tap_on
tap_on [1,2] do |obj|
  obj[4] = 5
end #=> [1, 2, nil, nil, 5]
make_new
make_new Hash do |obj|
  obj[1] = 2
end #=> {1 => 2}
Information Read more about using tap on the ruby best practices blog.
Specification (show)
require 'zucker/tap'
using Zucker::Tap


describe 'tap_on' do
  it 'should call tap on the argument and apply the block' do
    obj = "an_object"
    block = :reverse.to_proc

    tap_on( obj, &block ).should == obj.tap( &block )
  end
end

describe 'make_new' do
  it 'should create a new instance of the class given as argument, apply the block on it and return result' do
    make_new Hash do |obj|
      obj[1] = 2
    end.should.send(:'==', { 1 => 2})
  end
end
Source (show)
require 'zucker'

module Zucker
  module Tap
    refine Object do
      private

      def tap_on(obj, &block)
        obj.tap(&block)
      end

      def make_new(what, *args, &block)
        what.new(*args).tap(&block)
      end
    end
  end
end

unary_conversion

Summary Easy conversion between strings and symbols. Sometimes, you do not care if you get a String or Symbol as input. Instead of using AS' HashWithIndifferentAccess you can just always convert to your preferred format
Activate
using Zucker::UnaryConversion
Usage
String#+@
+'was_string' # => 'was_string'
String#-@
-'was_string' # => :was_string
Symbol#+@
+:was_symbol  # => 'was_symbol'
Symbol#-@
-:was_symbol  # => :was_symbol
Specification (show)
require 'zucker/unary_conversion'
using Zucker::UnaryConversion


describe Symbol, '#+@' do
  it 'should convert to_s' do
    +:matz == 'matz'
  end
end

describe Symbol, '#-@' do
  it 'should do nothing' do
    -:matz == :matz
  end
end

describe String, '#+@' do
  it 'should do nothing' do
    +'matz' == 'matz'
  end
end

describe String, '#-@' do
  it 'should convert to_sym' do
    -'matz' == :matz
  end
end
Source (show)
require 'zucker'

module Zucker
  module UnaryConversion
    refine String do
      def +@
        self
      end

      def -@
        to_sym
      end
    end

    refine Symbol do
      def +@
        to_s
      end

      def -@
        self
      end
    end
  end
end

Changelog

2014-04-01 | Zucker 100.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://rbjl.net/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