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, &:+)
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 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
SHORTCUTS (See http://ruby.wikia.com/wiki/Special_variable)
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'
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)