Ruby for Kittens
Ruby has Class
A Basic Ruby class
# <-- character causes remainder of line to be treated as a comment class Kitten < BaseClass @cvar = "Class Variable" # Declares a class variable with initial setting attr_assessor :ivar # Creates @ivar instance variable, o.name() and o.name=() attr_reader :ivar # Creates @ivar instance variable and o.name reader $global_var = 0 # Try to avoid global variables, okay? # Called after Kitten.new to initialize the object def initialize(*args) end # A Class method (General Kitten-related function) Called as: Kitten::method() def self.class_method(*args) @cvar # Returs a class variable end # An Instance method (specific kitten), called as kitten.says() def says @ivar = 1 # Access instance variables "Meow" # always returns last value or last statement end end kitten = Kitten.new() # Creates instance of Kitten kitten.says # => "Meow" Kitten.new.says # You can chain the methods together! # => Meow
Flow Control
if animal.says == "Meow" puts "Its a kitty!" elsif animal.says == "Woof" puts "Its a puppy!" else puts "I don't know what that is" end puts "hi puppy" if animal.says == "Woof" puts "Not a kitty!" unless animal.says "Meow" # Case statement, returns matching value, can be assigned to returned it_is_a = case animal.says when "Meow" then "kitty" when "Woof" then "doggie" else "i don't know" end # Loops: you shouldn't need to use them! See enumeration for i in 1..10 do next if i == 7 # Skips rest of iteration break if i == 8 # Ends loop redo if try_again # Restart iteration puts i end while condition do something end begin something while condition until condition do something end for variable in expression_list do # foreach something end
Blocks
Ruby’s Blocks are its most important and cool aspect, kitty. Every funtion (method) call can take an optional block to inject some customization into an abstract, generic routine!
Blocks can optionally take arguments, and are in two formats:
a.method { "hello" } # One-line block (by convention), returns value when called
a.method do # Multi-line block (by convention), returns value when called
"hello" # The last value references in a block or statement is returned
end
a.method { |arg| arg.upcase } # Blocks can accept arguments like methods
a.method do |arg|
arg.upcase
endOf course, blocks are mostly used for Ruby loops and enumerations (more info later….)
array.each { |item| puts item } # Iterates over, and prints each item in an array
records.each do |record| # Iterates over a set of records
record.something
endYou can save a block until later by using lambda to store the code. Its like an undercover function!
code = lambda {|*args} something }
code.call(1, 2, 3)Strings
name = "Maru" i = 12 name = "I am #{i} cat years old" # Interpolates value of i name = %Q(I am #{i} cat years old) # %Q for ", %q for ' (not interpolated) "123 boom!".to_i # => 123 (to integer, stopping at non-numeric) "asdf"[3] # => "f" Character at position "asdf"[2,2] # => "df" Substring name[2,2] = "RU" # a is now "MaRU" "hello".index('lo') #=> 3 name.empty? # True if length of zero "maru".capitalize # => Maru "Maru".upcase # => "MARU" "Maru".downcase # => "maru" "Résumé".size # => 6 (characters) "Résumé".bytes.count # => 8 (bytes in UTF-8) . "hello".gsub(/[aeiou]/, '*') # => "h*ll*" Global substitition by regex "hello " . "there" # => "hello there", operation concatenation
Numbers
123.class #=> FixNum, so its an integer 12.3.class #=> Float, a floater 020 #=> 16, its octal, baby! 0x32 #=> 50, hexidecimal dude 12.to_s # => "12", now a string
Ranges
1..10 # => A range of numbers, 1 to 10 (1..10).class # => Range, Syntax to operate on ranges (1..10).each {|i| puts i} # => Prints 1 through 10, a cool loop
Hashes
hash = Hash.new("default value") # Create hash with constructor hash = {"key" => "value", :key=>123, 1=>"one" } # Without constructor hash = { name:value } # New syntax, same as { :name => value } hash[:key] # => 123 hash[:newkey] = 234 hash.keys # Returns array of key names hash.delete("key") hash.delete_if { |key,value| key.blank? } # Deletes pairs returning true hash.each { |key, value| puts key, value } hash.empty? hash.fetch("key") { |key| puts "#{key} not found" } hash.has_key?(key) # alias: include?() key?() member?() hash = hash.merge(otherhash) hash.merge!(otherhash) hash.size # Alias: length()
Arrays
a = Array.new() # Create with Constructor a = [1, :hello, "there", object] # Without constructor a = %w(each space-delimited word is an element) # Also: %w{}, %w//, etc. a.first a[0] # first element a.last a[-1] # Last element a[start,length] # slice of array a[2..6] # 3rd through 7th elements using Range a.compact! # Removes nil items in array a.delete(value) # Deletes all items of the given value a.delete_at(index) a.flatten # Returns array with sub-arrays flattens into a single one a.include?(value) # True if value is in the array a.join(", ") # Returns string of values and separator "1, 2, 3" a.length # Also: size a.reverse # new array with items reversed a.sort {|a,b| a<=>b } # Sort with optional compare block a.uniq # New array without duplocated. also: uniq! a | otherarray # Union of arrays, removes duplicates a.push(value) # Appends to array v = a.pop # Removes and returns last item v = a.shift # Removes first item from list a.unshift(v) # Prepends value to left of array a = a.map { |item| item * 2 } # New array with items doubled a.delete_if {|item| item < 0 } a.each { |item| puts item } a.fetch(index) {|i| "puts index #{i} out of range" }
Enumeration
123.times do something end 123.times { |i| puts i } arr.each { |item| puts i } arr = arr.map {|item| item.upcase } # Runs block once per element, returns array of results (5..10).inject {|sum, n| sum + n } #=> 45, Loops, sends object for op, returns at end (5..10).r {|sum, n| sum + n } #=> 45, Loops, sends object for op, returns at end (1..10).reject {|i| i % 3 == 0 } #=> [1, 2, 4, 5, 7, 8, 10], those elements evaluated to false (1..10).reject {|i| i % 3 == 0 } #=> [1, 2, 4, 5, 7, 8, 10], those elements evaluted to true %w(rhea kea flea).sort #=> ["flea", "kea", "rhea"], sorted according to <=> operator (1..10).sort {|a,b| b <=> a} #=> [10, 9, 8, 7, 6, 5, 4, 3, 2, 1], reverse sort
Calling Methods
When you call a ruby method, there are several rules in effect
- Each pre-determined argument is listed. Defaults can be specified for those not given;
- Additional arguments can be gathered with the “splat” operatior “*args”
- Any hash-like “options” are gathered as the last item of the “additional arguments” array
- A passed block matches the name with the & operator, such as &block
To Illistrate:
def a(a) # Takes one argument def a(a, b=1) # Takes one argument, optional argument b defaults to 1 def b(a, b=1, *args) # Takes one argument, and a variables number of args into arrary "arg" def c(a,*args, &block) # ... as above, bit with an optional block options = args.last.is_a?(Hash) ? args.pop : {} # Take hash-option off of end of variable arguments end # a(1) # sets a=1 b(1, 2, 3) # sets a=1, b=1, args = [3] b(1, 2, :opt=>val) # ... sets args[1] = {opt=>val} c(1, 2, :opt=>val) {...} # ... sets args[1] = {opt=>val} and block ={...}
To pull a/an options has off of the argument list:
opt = args.last.is_a?(Hash) ? args.pop : {} # Assigns last (option) hash as opt hashTo send a operation to a given block:
if block_given? # checks for a called block yield block *args # calls the block, passing the arguments end #
Exceptions
Rubies begin…rescue…end constuct is designed to capture errors and handle them. At a mimumum, all you need is the begin….rescue…end, the other clauses are for when you do need them.
begin # Starts a block to catch and possibly resolve exceptions raise "Message" # Oh noes! Something unexptected happened, raise the error rescue Exception => e # Captures matching exception class, assigns to 'e' variable retry if tries < 3 # We can retry the block for a while, for timeouts, etc. puts "got #{e}" # rescue # Captures any other Exception puts "got exception" # else # This block is executed when there was no exception puts "no exception" # ensure # This block is ALWAYS executed, to close resources, etc. puts "always executed" # For instance, close files, connections, etc. end # Ends the block.
Throw and Catch are not for errors, but for execution out of bounds, an escape
catch(:symbolname) do # Captures throw :symbolname, returns the payload thrown # complex multi-level loops and conditions throw :symbolname, expression # Get out of here! Jump to end of catch block end # End of scope of throwable :symbolname x = catch(:x) { throw(:x, 23) } # Assigns 23 to x if we get in a mess within the block