Tuesday 30 April 2013

Ruby notes

Not being familar with ruby, here's a few notes / cheat sheet as I keep forgetting the basics

Functions:

def name (param1="Default Value", paramN, *args, &block)
  // actions
  return val
end

*args - A variable sized argument (arg.length)
&block - a block to be automatically converted to Proc

Naming conventions:

Type Example Notes
constant Pi Starts with capital letter. Is mutable!!!
boolean test isEql? Methods with a boolean response are prepended with a '?'
destructive test delete! Makes changes to object on which the function is invoked on (ends with '!')
setter method name=(n) method name ends in '='s sign
instance variable @counter .
class variable @@times .
global variables $global .

Symbols:

Placeholders for identifiers and strings, always prefixed by a ':' (colon). Can't be directly created - for Strings - use to_sym or intern methodsuse id2name to transform back.

Effectively transforms the string content into a label. Because of the interning symbols are more memory efficient then copies of strings.

name = "Matz"
name.to_sym # => :Matz
:Matz.id2name # => "Matz"
name == :Matz.id2name # => true

Conditionals:

if a == b then <do stuff> end
if a == b: <do stuff> end // : instead of 'then'
if a == b && b == c: <do stuff> end
puts "output" if a == b  // no need for then & end if used as a statement modifier

if a == 1
  puts 'one'
elsif a == 2
  puts 'two'
else
  puts '!(1 || 2)'
end

if a == :foo
  puts "test against symbol multi-line"
elsif a == :bar: puts "test against symbol single line (extra colon)"
end

case foo
  when :foo; "Foo"  // NOTE: the ':' operator was allowed in ruby 1.8
  when 1..3; "Range of value test"
  else "default"
end

Iteration:

while <condition> do   // 'do' is optional
  // code
end

begin
  // code
end while <condition>

for i in 1..5 do  // 'do' is optional
  print i, " "
end

// Alternatively, use 'times' method from the Integer class
5.times { |i| print i, " " }
// also there is upto and downto methods
2.upto(8) { |i| print i, " " }

unless - like a negated if statement
until - like a while loop, do until a condition is met
loop - creates a loop forever until a break condition is met (such as break statement)


Strings:

my_string = %{A string with customer delimiters defining start / end}
multi_line = <<#  // use -<< to keep indentation
  text across multiple
  lines with a custom delimiter
#
h = "Foo -".concat("bar") << "!" // concatenation methods
h.freeze // make string immutable, check with frozen? method
h[3] // access 3rd character code at index location (use h[3].chr for character)
h[start, len] // substring
h.index("b") // search for char
"a" <=> "a" // 'spaceship operator' contains character code of strings, returns 0, 1 or -1

== and eql? are slightly different(== returns true if two objects are Strings, eql? checks content and length)

Arrays:

Array.new || Array.new(12) || Array.new 12 || Array.new(12, <default>)
Array.new(10) { |e| e = e * 2 } // [0, 2, 4, ..., 18 ]
%w{ foo bar who har } // new array with whitespace separating entries
rg = Array(0..9) // [0, 1, 2, ..., 9]
rg = Array[ "one", "two", "three" ] || [ "foo", "bar" ]
rg[-1] // access last element in array, or rg.first / rg.last
rg.index("two") == 1 // lookup index of item
rg1 & rg2 // intersect of arrays (only common elements)
rg1 - rg2 // difference (only unique elements)
rg1 | rg2 // union (join with dupes removed)
rg.pop || rg.push || rg.shift || rg.unshift // stack operations

Array.clear
Array.empty? // true | false

Hash / Dictionary

Hash.new || Hash[ :key1, "Value1", ... ] || Hash[ :key1 => "Value1", ... ] 
Hash.new("month") // hash with a default value - the one returned if key has no value associated with it
h = {symbol: "value"} // 1.9 using symbol as hash key
h = {:symbol => "value"} // <1.9 ruby using symbol as hash key

h.has_key? :key1 || h.has_value? "Value1" || h[:key]
h.keys // list keys
h.values // list values

h.delete( :key )
h.delete_if { |k,v| v == "Value1" }
h.clear || h.empty?

h.each { |k,v| puts "#{k} / #{v}" } // loop through each key-value pair
h.each_key || h.each_value // just keys or values

Classes:

class Hello < Greeting // Hello extends (inherits) Greeting
  @@size = 0 // class variable

  attr :onlyget
  attr :getandset, true // second argument true
  attr_reader :gone, :here // getter
  attr_writer :count, :set // setters
  attr_accessor :start, :stop // accessors 
  def initialize( arg1 )  // c'tor
    @val = arg1   // @ == instance variable
  end

  class << self // singleton class, instantiated by the enclosing class
    def polygon(vertices, length)
      -- snip --
    end
  end

  def Hello.bye // Class method
    puts "Goodbye"
  end

  private  // methods below here are private (also can use protected)

  def hidden
    ... snip ...
  end

end

Hello.polygon(2, 5)

Hello.instance_methods - Object.instance_methods // print out the Hello specific methods

h = Hello.new( "Test" )
h.respond_to? (:gone)  // test if instance responds to method invocation

Blocks:

Nameless functions (effectively a closure). Can be wrapped in 'do' 'end' or or braces {}. Braces have higher precedence.
['purple', 'monkey', 'dishwasher'].each { |e| puts e }
or
['purple', 'monkey', 'dishwasher'].each do |element| puts element end
Automatically convert block to proc, prefix param with & (ampersand)

def return_proc( &proc )
  yield
end

return_proc { puts "Convert block to proc" }

Procs / Lambda:

Store procedures as objects complete with context. Invoke with call method (count.call)
count = Proc.new { [1,2,3,4,5].each do |i| print i end; puts } 
your_proc = lambda { puts "Lurch: 'You rang?'" }
my_proc = proc { puts "Morticia: 'Who was at the door, Lurch?'" }
Note: lambda / proc is preferred to Proc.new.

Modules:

like namespaces, a Module/class including a module inherits it's contents. 
Must be named as a constant (capital letter).

include Mymodule // if in same file
require 'Mymodule' // if module is in a separate file

Can define module methods by prefixing method with module name (e.g. def Mymodule.method)

Exception handling:

rescue analogous to catch, ensure analogous to finally in Java.
begin
  // do stuff
rescue ZeroDivisionError // type of exception
  // do stuff
ensure
  // do stuff
end

// throw / catch

def test(val)
  throw ( :zero ) if val <= 0
  return 5 / val
end

catch( :zero ) { test(-11) }

Metaprogramming:

write a program (or part of) using another program. 'define_method' in Module is key element - creates methods on the fly.

class Foo
  %w{ foo bar yar }.each do |n|
    define_method(n) { puts "method #{n}" }
  end
end

Foo.new.bar // prints 'method bar'

Miscellaneous:

  • alias - reference to another method. A way to have access to overridden methods. (alias <new-name> <existing-method>)
  • yield - Used within a method to execute a block associated with it. Can test for presence with block_given? test (def doit if block_given? yield else puts "no block" end end)
  • BEGIN / END can define blocks that execute before and after a program runs (BEGIN { puts "Date and time: " + Time.now.to_s } )
  • .class method returns type of object being called
  • irb - command line ruby interpreter
  • ri - command line ruby documentation, i.e. 'ri Array'