Zucker is the German word for sugar (pronunciation). It adds syntactic sugar in the form of independent, small scripts that make Ruby even more sweet. Read this blog post for a little introduction. See the github wiki for discussion and information about contributing.
gem install zucker # might need sudo
The gem consists of many small snippets, called cubes, which are bundled in packages. Currently, there are two packages available: default and debug. You can use a package be requiring it in this way:
require 'zucker/default'
and
require 'zucker/debug'
Since there aren't any dependencies within the gem, you could also pick only the cubes you want:
require 'zucker/egonil'
You can also lock your require to a specific version of Zucker by simply putting the version before the cube name in this way:
require 'zucker/1/egonil'. Future releases of the gem will include all previous (main) versions, so the behaviour of these directly required cubes will not change (except for critical bugs).
| Summary | A different way to create aliases: Reversed order and you can pass multiple alias names. |
|---|---|
| Why? | It's clearer in which order to put the arguments. |
| Methods/Usage | alias_for, aliases_foralias_for :methods, :ms # creates an alias for the method :methods with the name ms module Enumerable aliases_for :zip, :with, :% end # creates multiple aliases Module#method_alias_for, Module#method_aliases_for# same as alias_for, but using Module#alias_method instead of alias
|
| Further information | Thanks to Kristian Mandrup for the idea and alias method specs. |
| Specification | (show)
require 'zucker/alias_for' describe 'alias_for' do it 'should create an alias for global methods' do # rspec bug? # def m1 # 1 # end # alias_for :m1, :a1 # # proc do # a1.should == 1 # end.should_not raise_exception end it 'should create an alias for instance methods' do class Array def m2 2 end alias_for :m2, :a2 end proc do [1,2,3].a2.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 do Array.a3.should == 3 end.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 do 1.ma.should == 4 "1".mb.should == 4 [1].mc.should == 4 end.should_not raise_exception end end # alias_method module Blip def blip 'blip' end alias_methods_for :blip, :blap, :blup class << self def self_blip 'blip' end alias_methods_for :self_blip, :self_blap, :self_blup end end class Hello include Blip def hello 'hello' end alias_methods_for :hello, :hi, :howdy class << self def self_hello 'hello' end alias_methods_for :self_hello, :self_hi, :self_howdy end end describe 'alias_methods_for' do let(:h) { Hello.new } context "module context" do it "should alias instance methods" do h.blap.should == h.blip h.blup.should == h.blip end it "should alias class methods" do Blip.self_blap.should == Blip.self_blip Blip.self_blup.should == Blip.self_blip end end context "class context" do it "should alias instance methods" do h.hi.should == h.hello h.howdy.should == h.hello end it "should alias class methods" do Hello.self_hi.should == Hello.self_hello Hello.self_howdy.should == Hello.self_hello end end end |
| Source | (show)
def alias_for(m, *aliases) aliases.each{ |a| class_eval "alias #{a} #{m}" } end alias aliases_for alias_for class Module def alias_method_for(m, *alias_methods) alias_methods.each{ |a| class_eval do alias_method a.to_sym, m.to_sym end } end alias alias_methods_for alias_method_for end # J-_-L |
| Compatibility | 1.9, 1.8 |
| Discussion | github wiki |
| Summary | Some convenient aliases (and constants) for existing methods. |
|---|---|
| Methods/Usage | Object#is_an?Object.is_a?Enumerable#withEnumerable#zip Enumerable#%Enumerable#zip Array#**Array#product Hash#+Hash#merge Binding#[]Binding#eval Infinity1.0 / 0.0 NaN0.0 / 0.0 |
| Specification | (show)
require 'zucker/aliases' describe '(aliases)' do it 'should create these aliases' do # see aliases.rb for aliase list end it 'should define these constants' do Infinity.finite?.should == false NaN.nan?.should == true end end |
| Source | (show)
alias is_an? is_a? module Enumerable alias with zip alias % zip end class Array alias ** product end class Hash alias + merge end class Binding #alias [] eval def [](expr) self.eval "#{expr}" end end # constants - who would use these in real-world code for other things? Infinity = 1.0 / 0.0 # or 2*Float::MAX NaN = 0.0 / 0.0 # J-_-L |
| Compatibility | 1.9, 1.8 |
| Discussion | github wiki |
| Summary | Methods one could miss for Array. |
|---|---|
| Why? |
|
| Methods/Usage | Array#^[1,2,3,4] ^ [3,4,5,6] # => [1,2,5,6] Array#sum[1,2,3,4,5].sum # => 15 Array#chrs[72, 97, 108, 108, 111].chrs # => 'Hallo' |
| Specification | (show)
require 'zucker/array' describe 'Array#^' do it 'should do 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#sum' do it 'should sum the array' do [1,2,3,4,5].sum.should == 15 %w|More Ruby|.sum.should == 'MoreRuby' end end describe 'Array#chrs' do it 'should convert the array to a string, using each element as ord value for the char' do [72, 97, 108, 108, 111].chrs.should == 'Hallo' end end |
| Source | (show)
class Array def ^(other) # TODO: more efficient (self - other) + (other - self) end # can take an argument & block to be Rails compatible def sum(identity = 0, &block) # inject(:+) if block_given? map(&block).sum( identity ) else inject(:+) || identity end end def chrs self.pack 'C*' end end # J-_-L |
| Compatibility | 1.9, 1.8 |
| Discussion | github wiki |
| Summary | Calls the method named by the first paramenter and passes the other elements as paramaters. |
|---|---|
| Why? | When using Symbol#to_proc, you often wish to pass parameters. |
| Methods/Usage | Array#to_proc[1,2,3,4].map &[:*, 5] # => [5, 10, 15, 20] # you can also chain them, if the first parameter is an Array [1,2,3,4].map &[[:to_s, 2],[:+, 'b']] # => ["1b", "10b", "11b", "100b"] |
| Further information | Inspired by this article. More about to_proc. Chaining inspired by eregon. |
| Specification | (show)
require 'zucker/array2proc' describe 'Array#to_proc' do it 'should call the method of the first symbol, using the remaining elements as paramaters' do [1,2,3,4].map( &[:to_s, 2] ).should == ["1", "10", "11", "100"] end it "should convert each element to a proc and chain it, if the first parameter is an array" do [1,2,3,4].map( &[[:*,2],[:+,4]] ).should == [1,2,3,4].map{|i| i*2 + 4 } end end |
| Source | (show)
class Array def to_proc Proc.new{ |obj| if self.first.is_a? Array self.inject(obj){ |result, nested_array| nested_array.to_proc.call result } else obj.send *self end } end end # J-_-L |
| Compatibility | 1.9, 1.8 |
| Discussion | github wiki |
| Summary | Does pretty the same as in ActiveSupport (Every object can be asked if it is blank). |
|---|---|
| Why? | It's too familiar ;) |
| Methods/Usage | Object#blank?'an object'.blank? # => false |
| Specification | (show)
require '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)
class Object def blank? if respond_to? :empty? then empty? else !self end end def present? !blank? end end { # what to do # for which classes lambda{ true } => [FalseClass, NilClass], lambda{ false } => [TrueClass, Numeric], lambda{ empty? } => [Array, Hash], lambda{ self !~ /\S/ } => [String], lambda{ self == // } => [Regexp], }.each{ |action, klass_array| klass_array.each{ |klass| klass.send :define_method, :blank?, &action } } # J-_-L |
| Compatibility | 1.9, 1.8 |
| Discussion | github wiki |
| Summary | Creates a new instance of the class. |
|---|---|
| Methods/Usage | Class#to_proc[ [1,2],[3,5,6,7,3] ].map(&Set) # => [ Set[1,2], Set[5,6,7,3] ] |
| Further information | Inspired by Ruby Facets. More about to_proc. |
| Specification | (show)
require 'zucker/class2proc' require 'set' describe 'Class#to_proc' do it 'should create new instances of the class' do [ [1,2],[3,5,6,7,3] ].map(&Set).should == [ Set[1,2], Set[5,6,7,3] ] end end |
| Source | (show)
class Class def to_proc Proc.new do |*args| self.new *args end end end # J-_-L |
| Compatibility | 1.9, 1.8 |
| Discussion | github wiki |
| Summary | Creates a block, where nil does not raise NoMethodErrors. |
|---|---|
| Methods/Usage | egonil, nnegonil do nil.some_methods.that[:do].not.exist end # => nil nn(5){ nil.some_methods.that[:do].not.exist } # => 5 |
| Further information | See this post for more information and discussion. |
| Specification | (show)
require '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 return the nil_value if given' do egonil(9){ nil.some_methods.that[:do].not.exist }.should == 9 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 end |
| Source | (show)
def egonil(nil_value = nil) yield rescue NoMethodError => e if e.message =~ /NilClass$/ nil_value else raise NoMethodError end end alias nn egonil # J-_-L |
| Compatibility | 1.9, 1.8 |
| Discussion | github wiki |
| Summary | Enumerable extensions. |
|---|---|
| Methods/Usage | Enumerable#mash[1,2,3].mash{|e| [e, e.to_s] } # => {1=>'1',2=>'2',3=>'3'} |
| Further information | Inspired by Ruby Facets' mash. |
| Specification | (show)
require 'zucker/enumerable' describe 'Enumerable#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 |
| Source | (show)
module Enumerable def mash ret = {} each{ |kv| ret.store *( yield(kv)[0,2] ) } ret end end # J-_-L |
| Compatibility | 1.9, 1.8 |
| Discussion | github wiki |
| Summary | Some sugar for dealing with hashs. |
|---|---|
| Methods/Usage | Hash.zipHash.zip [1,2,3], [4,5,6] # => {1 => 4, 2 => 5, 3 => 6} Hash#<<{1 => 2} << [3, 4] # => { 1 => 2, 3 => 4 }
{1 => 2} << { 5=>6 } # => { 1 => 2, 5 => 6 }
|
| Further information | Some of the operators are inspired by Ruby Facets. |
| Specification | (show)
require 'zucker/hash' describe 'Hash.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 describe 'Hash#<<' do it 'should append 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 |
| Source | (show)
class Hash def self.zip(keys,values) Hash[ *keys.zip( values ).flatten ] end 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 end # J-_-L |
| Compatibility | 1.9, 1.8 |
| Discussion | github wiki |
| Summary | Use a hash to apply procs to specific objects. |
|---|---|
| Methods/Usage | Hash#to_proc[1,2,3,4].map(&{ 2 => lambda {|e| e + 1000}, 4 => :to_s, }) # => [1, 1002, 3, '4'] |
| Further information | More about to_proc. |
| Specification | (show)
require 'zucker/hash2proc' describe 'Hash#to_proc' do it 'should run the proc given in the value for a key in the hash' do [1,2,3,4].map(&{ 4 => :to_s, # 3 => [:to_s, 2] # "11" => if array2proc is used 2 => lambda {|e| e + 1000} }).should == [1, 1002, 3, "4"] end end |
| Source | (show)
class Hash def to_proc Proc.new{ |obj| if self.member? obj self[obj].to_proc.call obj else obj end } end end # J-_-L |
| Compatibility | 1.9, 1.8 |
| Discussion | github wiki |
| Summary | Access environment information with the Info module. |
|---|---|
| Why? | You don't need to remember in which global variable, constant or special method the information you are searching for is hidden. |
| Methods/Usage | Info# for example Info.current_file # __FILE__ Info.working_directory # Dir.pwd Info.load_path # $: Info.platform # RUBY_PLATFORM # you could also add them to the global namespace with: include Info # see the source file for the list of accessors or Info.list |
| Specification | (show)
require 'zucker/info' describe 'Info' do it 'should define accessors for global variables, constants and some special methods/keywords' do # see sourcefile for the list end end |
| Source | (show)
module Info class << Info # hash like access def [](what) send what end # list available info methods def list singleton_methods - [:[], :list, '[]', 'list'] end end module_function # current script file def current_file __FILE__ end def current_file_directory File.dirname(__FILE__) end # input def last_input_file $FILENAME end def last_input_line_number $. end def last_input $_ end # program def program_name $0 end def program_arguments $: end def loaded_programs $" end def program_data ::DATA end def child_program_status $CHILD_STATUS end # system info def environment ::ENV end alias env environment def working_directory Dir.pwd end def platform ::RUBY_PLATFORM end def process_id $$ end def load_path $: end # current def current_line __LINE__ end def current_method # 1.9 __method__ end def current_callstack caller end # dealing with strings def gets_separator $/ end def join_separator $, end def print_separator $, end def split_separator $; end # misc def security_level $SAFE end def warnings_activated? $VERBOSE end def debug_activated? $DEBUG end def last_exception $! end # defined objects def global_variables Object.send :global_variables end def global_constants Object.constants end # encoding (1.9) def source_encoding __ENCODING__ end def external_encoding Encoding.default_external end def internal_encoding Encoding.default_internal end # ruby version info def ruby_version ::RUBY_VERSION end def ruby_patchlevel ::RUBY_PATCHLEVEL end def ruby_description ::RUBY_DESCRIPTION end def ruby_release_date ::RUBY_RELEASE_DATE end end |
| Compatibility | 1.9, 1.8 (not all information) |
| Discussion | github wiki |
| Summary | Iterate over one or more collections. |
|---|---|
| Why? | It's like .each with two differences: It feels more like a control structure and you can easily iterate over multiple objects. |
| Methods/Usage | iterateiterate [1,2], [3,4,5] do |e,f| puts "#{e},#{f}" end # outputs # 1,3 # 2,4 # ,5 |
| Specification | (show)
require '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] # p 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 end end |
| Source | (show)
def iterate(*params) # params.shift.zip(*params).each{ |*elements| yield *elements } first = params.shift if params.empty? # single param - like each if block_given? first.map{|e| yield e } else first.map end else padded_first = Array.new( [first, *params].max_by(&:size).size ){|i| first[i] } # append nils obj = padded_first.zip *params if block_given? obj.map{|es| yield *es } else obj.map.to_enum end end end # J-_-L |
| Compatibility | 1.9, 1.8 |
| Discussion | github wiki |
| Summary | This method lets you easily assign lots of instance variables. |
|---|---|
| Why? | Often, you have to write boilerplate code for assigning instance varialbles, for example this one:def initialize(variable1, variable2) |
| Methods/Usage | instance_variables_from, ivarsdef a_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/ivars' describe 'instance_variables_from' do it 'should tansform the given parameter to instance variables' do def a_method(a = 1, b = 2) instance_variables_from binding # assigns @a and @b params = {:c => 3, :d => 4} ivars params # # assigns @c and @d end a_method @a.should == 1 @b.should == 2 @c.should == 3 @d.should == 4 end end |
| Source | (show)
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 alias ivars instance_variables_from # J-_-L |
| Compatibility | 1.9, 1.8 |
| Discussion | github wiki |
| Summary | Some useful general shortcut methods. |
|---|---|
| Why? | Readability. |
| Methods/Usage | activate_warnings!activate_warnings! # sets $VERBOSE to 1deactivate_warnings!deactivate_warnings! # sets $VERBOSE to 0executed_directly?, standalone?executed_directly? # checks, if the current file is run directly -> truelibrary?library? # checks, if the current file is run directly -> falseignore_sigint!ignore_sigint! # blocks CTRL+Cruby_1_8?ruby_1_8? # returns true for 1.8.xruby_1_9?ruby_1_9? # returns true for 1.9.xruby_1_8!, ruby_1_9!# raises a NotImplementedError if not executed with the right ruby version |
| Specification | (show)
require 'zucker/kernel' describe 'activate_warnings!' do it 'should set $VERBOSE to true' do activate_warnings! $VERBOSE.should == true end end describe 'deactivate_warnings!' do it 'should set $VERBOSE to false' do deactivate_warnings! $VERBOSE.should == false end end describe 'library?' do it 'should return false if the file is invoked directly' do library?.should == ( __FILE__ != $PROGRAM_NAME ) end end describe 'executed_directly?' do it 'should return true if the file is invoked directly' do executed_directly?.should == ( __FILE__ == $PROGRAM_NAME ) end end describe 'ignore_sigint!' do it 'should catch ctrl+c signals' do # ... end end describe 'ruby_1_8? and ruby_1_9?' do it 'should return true if run with the right ruby version' do # ... end end describe 'ruby_1_8! and ruby_1_9!' do it 'should throw a NotImplementedError if run with the wrong ruby version' do # ... end end |
| Source | (show)
module Kernel def activate_warnings! $VERBOSE = true end def deactivate_warnings! $VERBOSE = false end def library? __FILE__ != $PROGRAM_NAME end def executed_directly? __FILE__ == $PROGRAM_NAME end alias standalone? executed_directly? def ignore_sigint! # ctrl+c Signal.trap *%w|SIGINT IGNORE| end # version getter def ruby_1_9? RUBY_VERSION[2,1] == '9' end def ruby_1_8? RUBY_VERSION[2,1] == '8' end def ruby_1_9! if RUBY_VERSION[2,1] != '9' fail NotImplementedError, "Sorry, this script requires Ruby 1.9, but was executed with a different Ruby version!" end true end def ruby_1_8! if RUBY_VERSION[2,1] != '8' fail NotImplementedError, "Sorry, this script requires Ruby 1.8, but was executed with a different Ruby version!" end true end end # J-_-L |
| Compatibility | 1.9, 1.8 |
| Discussion | github wiki |
| Summary | Adds Object#mcopy to create a deep copy using Marshal. |
|---|---|
| Why? | Every Ruby book describes, you can do Marshal.load Marshal.dump object to create a deep copy... But who needs this verbose syntax in everyday coding? |
| Methods/Usage | Object#mcopya = %w[hello world]
b = a.mcopy
|
| Specification | (show)
require 'zucker/mcopy' describe 'Object#mcopy' do it 'create a (deep) copy via marshalling' do a = %w[hello world] b = a.mcopy b.should == a b[0][1,1] = '' b.should_not == a end end |
| Source | (show)
class Object def mcopy Marshal.load Marshal.dump self end end # J-_-L |
| Compatibility | 1.9, 1.8 |
| Discussion | github wiki |
| Summary | not returns an object on which all methods are redirected to it's receiver object, but returns inverted boolean. |
|---|---|
| Why? | Better readability. |
| Methods/Usage | Object#not[1,2,3].not.empty? # true |
| Further information | See this article by Jay Field for more information. |
| Specification | (show)
FIXME: missing |
| Source | (show)
class Object def not NotClass.new self end class NotClass < BasicObject def initialize(receiver) @receiver = receiver end def method_missing(m, *args, &block) not @receiver.public_send( m, *args, &block ) end end end # J-_-L |
| Compatibility | 1.9 |
| Discussion | github wiki |
| Summary | Use &/regex/ to match it against strings. |
|---|---|
| Methods/Usage | Regexp#to_proc%w|just another string array|.map &/[jy]/ # => ["j", nil, nil, "y"] %w|just another string array|.select &/[jy]/ # => ["just", "array"] |
| Further information | More about to_proc. |
| Specification | (show)
require 'zucker/regexp2proc' describe 'Regexp#to_proc' do it 'should match the regex' do %w|just another string array|.map( &/[jy]/).should == ["j", nil, nil, "y"] %w|just another string array|.select( &/[jy]/).should == ["just", "array"] end end |
| Source | (show)
class Regexp def to_proc proc do |e| e.to_s[self] end end end # J-_-L |
| Compatibility | 1.9, 1.8 |
| Discussion | github wiki |
| Summary | Creates a sandbox area. |
|---|---|
| Why? | Ruby comes with sandboxes, but they are hidden (-> integrated) in the $SAFE concept. |
| Methods/Usage | sandboxsandbox do # dangerous commands throw SecurityErrors ($SAFE=4) end # everything's normal again sandbox( lambda{} ) do # no Exception is thrown, if non-nil parameter is passed end # if it is a proc, it will be run instead, if an SecurityError gets raised |
| Specification | (show)
require 'zucker/sandbox' describe 'sandbox' do it 'should throw a SecurityError if bad commands are issued' do proc do sandbox do `ls` end end.should raise_exception SecurityError end it 'should not throw an exception for errors, if the given parameter is not nil' do proc do sandbox( true ) do `ls` end end.should_not raise_exception end it 'should run the proc passed as parameter for errors, if it is given' do sandbox( lambda{|e| e.class } ) do `ls` end.should == SecurityError end end |
| Source | (show)
def sandbox(rescueblock_or_default=nil) Thread.start do $SAFE = 4 yield end.value rescue SecurityError => e if !rescueblock_or_default.nil? if rescueblock_or_default.is_a? Proc rescueblock_or_default.call e else rescueblock_or_default end else raise e end end # J-_-L |
| Compatibility | 1.9, 1.8 |
| Discussion | github wiki |
| Summary | This helper methods defines [] and []= for accesing an instance variable. |
|---|---|
| Methods/Usage | square_brackets_forclass 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' 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)
def square_brackets_for(ivar, assignment = true) # undef [] if respond_to? :[] # undef []= if respond_to? :[]= #instance_eval do 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 # J-_-L |
| Compatibility | 1.9, 1.8 |
| Discussion | github wiki |
| Summary | String extensions. |
|---|---|
| Why? | Strings cannot be comfortable enough ;). |
| Methods/Usage | String#^'Yes vs No'^3 # => 'Yes' String#lchomp' Yes'.lchomp # => 'Yes' String#lchomp!# mutable lchomp versionString#ords'Hallo'.ords # => [72, 97, 108, 108, 111] 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 |
| Further information | constantize is an improved version of ActiveSupport's one |
| Specification | (show)
require 'zucker/string' 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 describe 'String#lchomp' do it 'should chomp on the left side' do string = 'Theoretische Informatik ist voll geil!' string.lchomp('T').should == 'heoretische Informatik ist voll geil!' end end describe 'String#ords' do it 'should unpack characters' do string = 'Theoretische Informatik ist voll geil!' string.ords.should == [84, 104, 101, 111, 114, 101, 116, 105, 115, 99, 104, 101, 32, 73, 110, 102, 111, 114, 109, 97, 116, 105, 107, 32, 105, 115, 116, 32, 118, 111, 108, 108, 32, 103, 101, 105, 108, 33] end 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 'Spec::VERSION'.constantize.should == Spec::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 NameError 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 NameError end end end |
| Source | (show)
class String def ^(pos) pos = pos.to_i if pos >= 0 self[pos..-1] else self[0...pos] end end def lchomp(arg = $/) self.reverse.chomp(arg).reverse end def lchomp!(arg = $/) self.reverse.chomp!(arg).reverse end def ords self.unpack 'C*' end 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 # J-_-L |
| Compatibility | 1.9, 1.8 |
| Discussion | github wiki |
| Summary | This cube adds the two tap variants tap_on (known as returning in ActiveSupport) and make_new. |
|---|---|
| Methods/Usage | tap_ontap_on [1,2] do |obj| obj[4] => 5 end #=> [1, 2, nil, nil, 5] make_newmake_new Hash do |obj| obj[1] = 2 end #=> {1 => 2} |
| Further information | Read more about using tap on the ruby best practises blog. |
| Specification | (show)
require '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 res = make_new Hash do |obj| obj[1] = 2 end res.should == { 1=>2 } end end |
| Source | (show)
def tap_on(obj, &block) obj.tap &block end def make_new(what, *args, &block) what.new(*args).tap &block end # J-_-L |
| Compatibility | 1.9 |
| Discussion | github wiki |
| Summary | Easy conversion between strings and symbols. |
|---|---|
| Why? | Sometimes, you do not care if you get a String or Symbol as input - but when analysing it, you often need to choose one format. A concise possibility for this conversion is using the unary operators String#-@ and Symbol#+@. |
| Methods/Usage | String#+@+'was_string' # => 'was_string' String#-@-'was_string' # => :was_string Symbol#+@+:was_symbol # => 'was_symbol' Symbol#-@-:was_symbol # => :was_symbol |
| Further information | Inspired by (I've seen the unary + for Symbol somewhere on the net... but cannot remember where...) |
| Specification | (show)
require 'zucker/unary' describe Symbol, '#+@' do it 'should convert to_s' do +(:matz) == 'matz' # () 1.8 bug 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)
class String def +@ self end def -@ to_sym end end class Symbol def +@ to_s end def -@ self end end # J-_-L |
| Compatibility | 1.9, 1.8 (+:literal not possible) |
| Discussion | github wiki |
| Summary | Easy creation of Regexp.unions. |
|---|---|
| Methods/Usage | Regexp#|, String#|/Ruby\d/ | /test/i | "cheat" # creates a Regexp similar to: # /(Ruby\d|[tT][eE][sS][tT]|cheat)/ |
| Specification | (show)
require 'zucker/union' shared_examples_for "Regexp.union operator" do it "should create an 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)
class Regexp def |(arg) Regexp.union self, arg.is_a?(Regexp) ? arg : arg.to_s end end class String def |(arg) Regexp.union self, arg.is_a?(Regexp) ? arg : arg.to_s end end # J-_-L |
| Compatibility | 1.9 |
| Discussion | github wiki |
| Summary | Easy debug printing with the p alternative .D. It outputs to stdout and returns self. Accepts a block. |
|---|---|
| Methods/Usage | Object#Dsome.D.methods.D.noone.D.knows.D # ...outputs 4 lines with the inspected objects # => (result) 21+Math.sin(42).D # outputs -0.916521547915634 # => 20.0834784520844 name = 'Earth' 'Hello ' + name.D{|e| "The length is: #{e.size}"} # outputs "The length is: 5" # => 'Hello Earth' |
| Further information | This is inspired by the .tap method.There is also tap cube with more tap methods. |
| Specification | (show)
require 'zucker/D' describe 'Object#D' do let :a do [ 1, "1", 2..5, [], {:hallo => :du}, nil, true ] end it 'should not change the object"s value' do a.each{ |e| (e.D).should == e } a.each{ |e| (e.D{|value| "This is a: #{value}"}).should == e } end it "should puts .inspect if no block is given (and not change the object's value)" do capture_stdout do a[0].D a[1].D a[6].D end.should == %{1\n"1"\ntrue\n} end it "should puts the block if it is given (and not change the object's value)" do capture_stdout do a[0].D{|value| "This is a: #{value}"} a[1].D{|value| "This is a: #{value}"} a[6].D{|value| "This is a: #{value}"} end.should == %{This is a: 1\nThis is a: 1\nThis is a: true\n} end end |
| Source | (show)
module Kernel def D(*args, &block) if args.empty? tap{ if block_given? puts yield self else puts self.inspect end } else raise ArgumentError, ".D - The parser thought that the code after .D are method arguments... Please don't put a space after D or use .D() or .D{} in this case!" # eval ... end end end # J-_-L |
| Compatibility | 1.9, 1.8 |
| Discussion | github wiki |
| Summary | Adds a verbose Binding#inspect. |
|---|---|
| Why? | It gives you information about the current environment. |
| Methods/Usage | Binding#inspectbinding.inspect # shows your current environment |
| Specification | (show)
require 'zucker/binding' describe 'Binding#inspect' do it 'should output the current environment' do #pending '#TODO: write spec' end end |
| Source | (show)
class Binding def inspect put_vars = lambda { |array| if array.empty? ' - none' else array.map{|e| " - #{e}: #{ self.eval '#{e}'}"}*"\n" end } "#{self.to_s} local vars #{ put_vars[ self.eval 'local_variables' ] } (instance vars) #{ put_vars[ self.eval 'instance_variables' ] } self - #{self.eval 'self'} block_given? - #{self.eval 'block_given?'}" end end # J-_-L |
| Compatibility | 1.9, 1.8 |
| Discussion | github wiki |
| Summary | mm displays an ordered public method list. |
|---|---|
| Why? | See one object's methods without those rarely used inherited methods. |
| Methods/Usage | Object#method_list, Object#mm'test'.mm 2 # outputs the list (2 levels deep) |
| Further information | See this article for more information. |
| Specification | (show)
require 'zucker/mm' describe 'Object#method_list' do it 'should display an ordered method list' do #pending '#TODO: write spec' end end |
| Source | (show)
module Kernel def method_list(levels = 1) if self.is_a? Module klass, method_function = self, :public_methods else klass, method_function = self.class, :public_instance_methods eigen = self.singleton_methods if !eigen.empty? puts :Eigenclass # sorry for not being up to date, I just love the word p self.singleton_methods end end levels.times{ |level| if cur = klass.ancestors[level] p cur # put class name p cur.send method_function, false # put methods of the class else break end } self # or whatever end alias mm method_list end # J-_-L |
| Compatibility | 1.9, 1.8 (returns strings instead of symbols) |
| Discussion | github wiki |
2010-08-14 | Zucker 3 * added tap cube * added Object#not * added alias_for for an alternative alias syntax * added String#constantize (improved AS version) * improved Info module * made Array#sum Rails compatibile * improved docs * changed directory layout (no changes for requiring) * more small changes 2010-08-08 | Zucker 2 * added info cube * added chaining for array2proc * fixed Hash.zip * fixed 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