Skip to content

Instantly share code, notes, and snippets.

@AMHOL
Last active December 17, 2015 04:49
Show Gist options
  • Save AMHOL/5553433 to your computer and use it in GitHub Desktop.
Save AMHOL/5553433 to your computer and use it in GitHub Desktop.
Cool Ruby snippets

USEFUL TRICKS

Array#reduce

Array.reduce will inject and initial value into a block and pass in each value in turn until all values in the array has been iterated i.e

([1] * 10).reduce(0) { |total, current_value| total + current_value } #= 10
# Inject does the same
([1] * 10).inject(0) { |total, current_value| total + current_value } #= 10

However, if we simply require a sum of all values in the array, we can take advantage of Symbol#to_proc as follows:

([1] * 10).reduce(:+) #= 10 # For Ruby 1.8.7 : ([1] * 10).reduce(0, &:+)
([1] * 10).inject(:+) #= 10 # For Ruby 1.8.7 : ([1] * 10).inject(0, &:+)
Hash#initialize with blocks

We can pass a block to Hash.new to define how values are added to the hash, i.e.

The following will make a request to a url when it is added as a hash key and save the response body

require 'net/http'
http = Hash.new{|h,k| h[k] = Net::HTTP.get_response(URI(k)).body }
http['http://www.google.com'] # makes a request
http['http://www.google.com'] # returns cached value
Proc#arity

Proc#arity returns the number of arguments passed to a Proc, we can use this to distinguish how the Proc should be involked, i.e. if it has an argument, pass the current class as the first argument, otherwise, use instance_eval to call the procedure in the current scope i.e.

In the following code we need access to the Person.full_name method, this is within the scope from which Document.generate is called so we need to pass document to the block as an argument

class Person
  attr_accessor :first_name, :last_name
  
  def initialize(f_name, l_name)
    @first_name, @last_name = f_name, l_name
  end
  
  def full_name
    "%s %s" % [first_name, last_name]
  end
  
  def save_full_name
    Document.generate('full_name.txt') do |doc|
      doc.prepend_lines = 'Name: '
      full_name
    end
  end
  
end

class Document

  attr_accessor :prepend_lines

  def self.generate(file, *args, &block)
    document = self.new(*args)
    content = block.arity == 0 ? document.instance_eval(&block) : block.call(document)
    document.write(file, content)
  end

  def write(name, content)
    #File.open(name, 'r+') { |f| f.write(prepend_lines + content) }
    puts prepend_lines.to_s + content.to_s
  end
  
end

dave = Person.new('Dave', 'Loper')
dave.save_full_name

# However, if we don't need access to anything in the scope from which Document.generate is called
# we can use the no arguments (instance_eval) method

Document.generate('random.txt') do
  self.prepend_lines = 'Random number between 1 and 1000: %d' % [Random.rand(1..1000)]
end
$:

Ruby $: returns an array containing each load path Ruby will search when loading classes, it can be used along with the unshift method to prepend path onto the Ruby load path, like so:

$:.unshift activesupport_path
$&

Ruby $& returns the matched string from the last Regular Expression test, or nil if no RegEx has been matched

$& #= nil
/test/i =~ 'testing'
$& #= 'test'
$`

Ruby $` returns the portion of the string before a match is made, or nil if no RegEx has been matched

$` #= nil
/test/i =~ 'retesting'
$1 #= 're'
$'

Ruby $' returns the portion of the string after a match is made, or nil if no RegEx has been matched

$' #= nil
/test/i =~ 'retesting'
$1 #= 'ing'
Regex array accessor

Ruby has an interesting array accessor on string, where if we are using a regular expression with capture, we can return matches with String[RegEx,matchNumber] i.e.

'testing'[/(te)(st)/,0] #= 'test' (matched portion)
'testing'[/(te)(st)/,1] #= 'te' (first match)
'testing'[/(te)(st)/,2] #= 'st' (second match)

Resources

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment