Skip to content

Instantly share code, notes, and snippets.

@dekom
Created March 21, 2012 01:01
Show Gist options
  • Save dekom/2143259 to your computer and use it in GitHub Desktop.
Save dekom/2143259 to your computer and use it in GitHub Desktop.
[NOTES] The Pickaxe 1.9

Chapter 5: Syntax Convention

The first characters of a name indicate how the name is used.

  • Begin with lowercase or underscore: local variables, method parameters, and method names
  • Begin with uppercase: class names, module names, and constants
  • Append with dollar sign ($): global variables
  • Append with at sign (@): instance variables
  • Append with double at sign (@@): class variables

Allowed characters:

  • All:
  • letters (exception @)
  • digits
  • underscores
  • Instance variables: underscores_between_words
  • Class names: CamelCase
  • Method names: may end with ?, !, =

Objects


Ruby is entirely object-oriented. Objects instances are created using the constructor <Object>.new.

Characteristics of object instances

  • object identifier (object ID)
  • instance variables
  • instance methods

Strings

String literals are created either from single-quote ' or double-quotes ".

In the single-quote construction, Ruby processes very little of the content, creating a true literal string. While the double-quote construction involves more Ruby processing.

For double-quote strings, Ruby will:

  1. Look for substitutions \n, \t and replace with binary value
  2. Look for expression interpolation. #{expression}

Arrays and Hashes

Both are indexed, non-homogeneous collections, using key to store value.

Arrays

Arrays are more efficient.

  • Constructor: a[0] = "an"
  • Keys are integers
  • Referenced using array[0]
%w{ an array of words } => ["an", "array", "of", "words"]

Hashes

Hashes are more flexible

  • Constructor:
  • { key => value }
  • Hash.new(0) => sets the return value of a non-existent key to 0
  • Keys are any object
  • Keys must be unique
  • Referenced using hash[key]

Symbols

They are constant names that do not need to be predeclared and that are guaranteed to be unique, regardless of location of appearance.

A symbol begins with a colon : and followed by a name. :north

Most notably used as keys in hashes

Special syntax for creating a hash with symbols as keys

inst_section = {  
  cello:       'string',  
  clarinet:    'woodwind',  
  drum:	       'percussion',  
}

inst_section[:cello] #=> string

Control Structures

Ruby has all the standard control flow structures.

if () ... 
elsif () ... 
else ... 
end
while () ... 
end

statement modifiers: useful shortcut for a single expression flow structure.

puts 'Hello, world!' if noob.level < 1

Regular Expressions

Pattern matching in strings. Remember, regular expressions are objects as well.

  • Creating a pattern: /pattern/
  • Matching a pattern: =~, which returns the patten's starting position if found, nil otherwise.

Visit Rubular for awesome regex practices.

Blocks and Iterators

Code blocks: chunks of code you can associate with method invocations, almost as if they were parameters.

Though powerful, they're simply chunks of code between

{ ... code.to_do ... }`
or `do ... code.to_do ... end

{} (brace) syntax has higher precedence than do ... end blocks. Regardless, the emerging convention has brace syntax for single-line blocks and do/end for multiline blocks.

Usage

method(parameters[]) { code.to_do }

Once such block has been passed to the method, it is called within the method using yield:

def call_block
    puts "Start of method"
    yield
    yield
    puts "End of method"
end

call_block { puts "In the block" }

produces:

Start of method
In the block
In the block
End of method

Parameters can be passed to the block between the vertical bars | params ... |

def call_block
    puts "Start of method"
    yield("In the block)
    puts "End of method"
end

call_block { |s| puts s }

Input/Output (I/O)

Inputs: get

Outputs: puts, printf, print

Command-Line Arguments

Either ARGV or ARGF.

Chapter 6: Classes, Objects, and Variables

Objects are created from classes to be program representation of real-life items to be manipulated.

Classes are defined:

class ClassName
end

Objects of each class are created:

a_class = ClassName.new
another_class = ClassName.new

Each time that an instance of an object is created, the class's initialize method is called.

class ClassName
    def initialize(parm0, parm1)
         @parm0 = parm0
         @parm1 = parm1
    end
end

This method is called mostly to setup default values of each object instance.

Likewise, every object in Ruby has a standard message method, to_s, that can be overwritten to output human-readable string representation of the object.

Objects and Attributes

Attributes are the externally visible facets of an object. These are usually manipulated by 'getter' (or accessor) methods and 'setter' methods.

Accessor methods can be created with attr_reader :accessor0, :accessor1, which functions the same as:

def accessor0
    ...
end

def accessor1
    ...
end

Setter methods can be created with attr_writer :setter0, which functions the same as:

def setter0=(value)
    ...
end

Partial to Ruby, any method names that ends in = can be accessed as className.setter0 = value. This abstracts out the difference between accessing variables and accessing methods.

However, since one would want to have both read and write access for a given attribute, Ruby also offers this method creation: attr_accessor :name, which creates both getter and setter methods.

Classes Working with Other Classes

To import additional classes, call (for example)

require 'csv'
require_relative 'className'
class CurrentClass
    ...
end

require_relative uses the current class's directory as file path to look for the class name.

Access Control

Controls for accessing your classes are done (primarily) through setting the accessibility of your methods. Methods can be either public - callable by everyone and default for all methods except initialize, protected - callable by the class itself and sub-classes (not class instances!), private - callable only by class instance self.

Methods can be defined as private (for example) by one of two ways:

private
    def method0
        ...
    end
    
    def method1
        ...
    end

or

def method0
    ...
end
def method1
    ...
end
    ...
private :method0, :method1

Either declaration makes method0 and method1 private methods.

Chapter 7: Containers, Blocks, and Iterators

The two main container classes in Ruby are Arrays and Hashes. Combine them with blocks, which are chunk of codes that can be passed around, they can easily and quickly create iterators.

Arrays

Arrays are created as literal arrays - ["a", "b", "c" ...] or using standard Array.new constructor.

Array elements are referenced using indices within [].
List of array access attributes:

  • Negative indices counts backwards.
  • Pair of numbers [start, count] returns an array starting at index start and contains up to count number of elements.
  • Ranges [start...end] returns an array starting from index start and ends at index end. Ranges can be negative.

Array element assignment is a[1] = value as well as working with pairs and ranges selections.

Hashes

Hashes are similar to arrays except that the keys can be any object, as opposed to strictly Integers in Arrays.

Hashes are created using the standard constructor or hash literal - {'a' => 'z', 'b' => 'y' , ...}.

Ruby 1.9 introduced new syntax for defining a key => value association with symbols as key: key: value.

Likewise, Hash in ruby 1.9 remembers the insert order, which isn't a guarantee in other language libraries.

Word Frequency: Hashes and Arrays

When passing a parameter into Hash.new(0), the parameter becomes the object's default value for unavailable keys. For example:

hash = Hash.new(0)
puts hash['word'] #=> 0
hash['word'] += 1
puts has['word'] #=> 1

Since hash remembers the order of insertion, in order to sort element by another parameter, we must call the Hash instance's sort_by element.

Blocks and Iterators

Blocks are chunks of code that can be passed around, while iterators invoke block of code.

Blocks

A block is simply a chunk of code enclosed between either braces or the keywords do and end.

Like a method, blocks can take parameters by having |varaible0, variable1| in the beginning of the block (I call them pipes).

Key notes on blocks is variable scoping. As long as variable within the block shares the same name as a variable outside of it, it will change the said variable. In ruby 1.9, however, all block parameters are local, and by adding a semicolon prior to a local variable within the 'pipes', e.g. [1, 2, 3, 4].each do |value; square|, then the variable becomes local to the block, in this case square is block local.

Blocks can be converted to the class Proc by placing a &variable_name as one of the parameters. This is the implementation behind the lambda (which takes in a block) and -> (ruby 1.9 synonymous operator for lambda).

Blocks can also be used for closures, which is when 'variables in the surrounding scope that are referenced in a block remain accessible for the life of that block and the life of any Proc object created from that block'.

This is best explained in an example

def n_times(thing)
    lambda {|n| thing * n}
end
p1 = n_times(23)
p1.call(3) #=> 69
p1.call(4) #=> 92
p2 = n_times("Hello")
p2.call(3) #=> "Hello Hello Hello "

In the previous examples, 23 and "Hello" are in the closure and were able to be referenced even outside of the method definition for n_times.

For all intents and purposes, blocks are as flexible as methods regarding how and what can be taken into.

Implementing Iterators

'A Ruby iterator is simply a method that can invoke a block of code.'

Block codes in iterators are called using the yield statement.

One can imagine yield as an interrupt in the control flow of the code, where the control is passed to the block of code and then back.

Useful, default iterators (look into Ruby API for details): .each, .each_with_index, .collect, and the mind-numbing .inject, which is (apparently) similar to the foldl function of SMLNJ.

Ruby 1.9 has an Enumerator object that can be crated by calling to_enum or enum_for on a collection.

Most of the internal iterator methods will also return an Enumerator object if called without a block.

Look through the Ruby API on the Enumerator class. Especially checkout .enum_for(:method_name).

.enum_for is used to create a custom Enumerator, where a particular method is called from within.

Enumerators Are Generators and Filters

(Advanced material)

Going to read this at a later time

Blocks for Transactions

Blocks can be used to section the logical work, and transfer control from one state to the next.

Blocks can be Objects

If the last parameter of a method begins with an ampersand (&), that parameters will store the Proc object, which is the object class for blocks.

Ruby provides two built-in methods that convert blocks into Proc objects:

lambda {block}

Proc.new {block}

Additionally, in Ruby 1.9, you can create Proc objects by:

proc = -> arg { puts "Proc with arg #{arg} }

Chapter 8: Sharing Functionality: Inheritance, Modules, and Mixins

The interesting chapter!

Inheritance and Messages

Inheritance allows you to create a class that is a refinement or specialization of another class. The original is the superclass of the created class, and the created class is the subclass of the original. (Or commonly referred as parent and child classes).

The child class inherits all of the parent's public and protected instances methods.

Syntax

class Child < Parent
    ...
end

Every object in Ruby is the child (multiplied by many generations) of the BasicObject, which is a "blank canvas" object created in ruby 1.9. For most functional cases, the root of the 'family tree' is Object.

To call superclass methods

class Class < Parent
    def method_name()
        super()
    end
    ...
end

super() calls the parent class's method that matches the name of the child's method.

Modules

Modules are a way of grouping together methods, classes, and constants.

Modules:

  • provide a namespace and prevent name clashes.
  • support he mixin facility

A mixin is 'like a partial class definition' and a combination of flexibility of multi-inheritance and simplicity of single-inheritance.

Namespaces

Modules are used to handle method name conflicts.

A module is defined by

module AModule
    ...
end

Then conflicting methods are called with AModule.method_name.

Note: Modules are not classes. Therefore, they cannot have instance methods (methods that are defined by def self.method_name.

Mixins

"At a stroke, they (modules) pretty much eliminate the need for inheritance."

Mixins are created when modules are included in classes.

module ModuleName
    def module_method
         ...
    end
end

class ClassName
    include ModuleName
    def class_method
        ...
    end
end

an_class_instance = ClassName.new
an_class_instance.module_method

By including a module in a class, that class has access to all of the module's instance methods (even though modules cannot have instances because they're not classes).

Note: in order to use include ModuleName, one may need to first load or require the file that contains the module.

Inheritance, Mixins, and Design

When to use what?

  • Inheritance: when it is a is-a relationship.
  • Mixins: when it is a has a or use a relationship.

Chapter 9: Standard Types

Numbers

Fixnum, Bignum, Float, Rational, Complex

Creating Numbers

prefix the following to a number for the specific formats:

octal: 0 decimal: 0d hex: 0x binary: 0b

Rational numbers: Rational(numerator, denominator)

Complex numbers: Complex(real, imaginary)

Operation

use require 'mathn' package to get most natural result of a mathematical operation.

Strings

sequence of characters, usually created with string literal

Construction

string literal or

%q, %Q or just % and here documents.

%q(arg) => 'arg', %Q(arg) = %(arg) => "arg"

Note that the character following %Q is just the delimiter, which can be any non-alphanumeric or non-multibyte character.

here documents:

string = <<END_OF_STRING
The body of the string is the input lines
up to one starting with the same test that
followed the '<<'
END_OF_STRING`

Here, terminating delimiter can be indented if << is changed to <<-

Encoding

default encoding is the encoding of the source file or US-ASCII

Chracters (string length 1) can be created with the ? operator: ?\C-a # => (control a. Not important, easier to use hex or string.

Ranges

used in ruby to implement sequences, conditions, and intervals

Ranges as Sequences

constructors: .. and ...

Any class can use these constructors as long as <=> (comparison method), succ (how to generate the next element) methods are defined within the class.

Ranges as Conditions

can be used to turn a condition to true on the first successive match and then turn it to false on the second success match:

while line = ets
    puts line if line =~ /start/ .. line =~ /end/
end

This prints a sequence of lines where the first line has the word start and the last line has the word end.

Ranges as Intervals

Intervals can be tested for contained elements with the === operator:

(1..10) === 5 # => true. Note that (1...10) === 9.9 is true while (1..9) is false.

can be used in case statement for conditional test

case test
when 0...1
    ...
else
    ...
end

Chapter 10: Regular Expressions

A regular expression is a pattern that can be matched against a string.

  • You can test a string to see whether it matches a pattern.
  • You can extract from a string the sections that match all or part of a pattern.
  • You can change the string, replacing parts that match a pattern.

Ruby's Regular Expressions

Create using forward slashes, e.g. /cat/.

Matching Strings with Patterns

Match a string against a pattern using =~. It returns the character offset into the string at which the match occurred:

/cat/ =~ "dog and cat" #=> 8
/cat/ =~ "catch" #=> 0
/cat/ =~ "Cat" #=> nil

To test whether a pattern does not match a string, using !~.

Changing Strings with Patterns

The sub method takes a pattern and some replacement text. If a match if sound, it replaces the matched substring with the replacement text.

str = "Dog and Cat"
new_str = str.sub(/Cat/, "Gerbil")
puts new_str #=> Dog and Gerbil

Use gsub to replace all the patterns, versus the first pattern for sub.

Digging Deeper

Regular expressions are just instances of the class Regexp.

Other ways to create Regexp instances: Regex.new(str) or %r{...}.

Regular Expression Options

Options that modify the way the pattern matches strings are placed at the end of literal terminators or as second parameters of .new.

  • i: Case Insensitive
  • o: Substitute Once. Any #{...} substitutions in a particular regular expression literal will be performed just once, the first time it is evaluated. Otherwise, the substitutions will be performed every time the literal generates a Regexp object.
  • m: Multiline Mode. Normally, . matches any character except a newline. With this option, . matches any character.
  • x: Extended Mode. Complex regular expressions can be difficult to read. This option allows you to insert spaces and newlines in the pattern to make it more readable. You can also use # to introduce comments.

Matching Against Patterns

Once a Regexp instance is created, match against strings using =~, !~, or Regexp#match(str).

Deeper Patterns

Time to study the special characters: ., |, (, ), [, ], {, }, +, \, ^, $, *, and ?.

Anchors

^ matches the beginning of the line and $ matches the end of a line.

\A matches the beginning of a string and \z and \Z # matches pre \n matches the end of a string.

\b matches word boundaries (word characters surrounded by non-word characters) and \B matches non-word boundaries (word characters surrounded by other word characters).

Character Classes

A character class is a set of characters between brackets [] that's to be matched in the string.

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