.. vim: set ft=rst: ===================== Ruby Course Notes ===================== :author: Dave Kuhlman :contact: dkuhlman (at) davekuhlman (dot) org :address: http://www.davekuhlman.org :revision: 1.1.1 :date: |date| .. |date| date:: %B %d, %Y :Copyright: Copyright (c) 2013 Dave Kuhlman. All Rights Reserved. This software is subject to the provisions of the MIT License http://www.opensource.org/licenses/mit-license.php. :Abstract: This document provides notes and an outline of an introductory course on programming in Ruby. .. sectnum:: :depth: 4 :start: 1 .. contents:: :depth: 4 Introductions Etc. ==================== Resources ----------- Look here for help: - `Ruby home -- http://www.ruby-lang.org/ `_ - `Ruby documentation -- http://www.ruby-lang.org/en/documentation/ `_ Preliminaries --------------- Text editor -- Use any good programmer text editor. See: - http://en.wikipedia.org/wiki/Text_editor - http://en.wikipedia.org/wiki/Comparison_of_text_editors The Ruby interactive shell -- irb ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Some notes: - ``irb`` can be configured from the command line or in your personal configuration file ``.irbrc`` in your home directory. - For help with configuring ``irb``, see chapter "Interactive Ruby Shell" in `Programming Ruby The Pragmatic Programmer's Guide -- http://www.ruby-doc.org/docs/ProgrammingRuby/ `_. Or, (on Linux), use ``man``:: $ man irb - For a more simple prompt, try:: $ irb --prompt=simple Or, put the following in your ``~/.irbrc`` file:: IRB.conf[:PROMPT_MODE] = :SIMPLE - If tab completion is not working for you, try adding the following to your ``~/.irbrc`` file:: require 'irb/completion' My ``.irbrc`` also saves history across sessions. Here is my complete ``.irbrc``:: IRB.conf[:PROMPT][:SIMPLE_DAVE] = { # name of prompt mode :PROMPT_I => ">> ", # normal prompt :PROMPT_C => ">> ", # prompt for continuated statement :PROMPT_S => nil, # prompt for continuated strings :RETURN => " ==>%s\n" # format to return value } #IRB.conf[:PROMPT_MODE] = :SIMPLE IRB.conf[:SAVE_HISTORY] = 100 IRB.conf[:HISTORY_FILE] = "#{ENV['HOME']}/.irb-save-history" require 'irb/completion' require 'irb/ext/save-history' require 'pp' # Make ruby ``print`` act more like Python ``print``. # Set the field separator and the record separator special variables. $, = " " $\ = "\n" def dir obj puts "Class: #{obj.class} object_id: #{obj.object_id}" puts "Methods:", obj.methods.sort.inspect end Displaying values, variables, etc -- Use ``puts``, ``print``, and ``p``: - ``puts`` adds a newline. - ``p obj`` writes ``obj.inspect`` and a newline. When you are debugging code, it's likely that it's ``p`` that you want. - ``print`` does *not* add a newline. - You can modify the behavior of ``print a, b, c`` by setting the values of ``$,`` and ``$\\`` (the field separator and the record separator variables). Example:: >> $, = "::" ==>"::" >> $\ = "\n-----\n" ==>"\n-----\n" >> >> >> print 11, 22, 33 11::22::33 ----- ==>nil >> Or, it might be more useful to set the field and record separators as follows:: >> $, = " " ==>" " >> $\ = "\n" ==>"\n" >> print 11, 22, 33 11 22 33 ==>nil >> You can do this in your ``.irbrc`` file. - If you start ``irb`` with this command ``$ irb -rpp``, then you can use ``pp obj`` to pretty print objects. - I do this ``print some_obj.methods.sort`` *a lot*. If you are like me in that way, you can consider adding something like the following to your ``.irbrc`` file, so that it will always be available at the ``irb`` interactive prompt:: def dir obj puts "Class: #{obj.class}" puts "Methods:", obj.methods.sort.inspect end Then use ``dir(some_object)`` (with or without parentheses) to display the methods supported by an object. Running a Ruby script ~~~~~~~~~~~~~~~~~~~~~~~ Run and test a script with one of the following. The second form enables you to use the Ruby debugger:: $ ruby my_script.rb ... $ ruby -rdebug my_script.rb ... Also see `Running Ruby scripts`_. Additional help and documentation ----------------------------------- Here are some places and ways to get help: - If the Ruby information system is installed on your system, you can get help from the command line. For example:: $ ri Array.sort = Array.sort (from ruby core) o o o - From the document you are currently reading. - At the Ruby documentation Web site: http://ruby-doc.org/ - Documentation on Ruby's standard classes is here: http://ruby-doc.org/core-2.0/index.html - "Programming Ruby The Pragmatic Programmer's Guide": http://www.ruby-doc.org/docs/ProgrammingRuby/ - "Ruby programming" at the Wikibooks Web site: http://en.wikibooks.org/wiki/Ruby_Programming - "The Ruby User's Guide": http://www.rubyist.net/~slagell/ruby/index.html - "Ruby Essentials": http://www.techotopia.com/index.php/Ruby_Essentials Running Ruby scripts ---------------------- Run a Ruby script as follows:: $ ruby my_script1.rb $ ruby my_script2.rb arg1 arg2 arg3 On Linux and Unix-like systems, to make the script itself executable, do the following: 1. Add the following as the first line of the script:: #!/usr/bin/env ruby 2. And, change the permission of the file to executable. To do so, either use your file manager, or from the command line do something like this:: $ chmod u+x my_script.rb In order to make your script both "run-able" and usable as a library (with ``require``), use the following at the bottom of the script to start processing when it is run, but *not* when it is imported (with ``require``):: if __FILE__ == $0 main() end Notes: - The above example assumes that when you run this script, you want to call the function ``main``. - The command line arguments are available within your script in the pre-defined variables ``ARGV`` or ``$*``. - If you need to parse and process command line options (prefixed with "-" or "--"), consider using the ``optparse`` module. - In order to use the Ruby debugger, run your script with the ``-rdebug1`` flag, for example:: $ ruby -rdebug my_scipt.rb Also see `Debugging ruby scripts`_ Inspecting and examining Ruby objects --------------------------------------- The following may be helpful:: >> p obj >> puts obj.methods.sort.inspect And, you could consider adding the following definition to your ``.irbrc`` file:: # A quick way to display object info and a sorted list of its methods. def dir obj puts "Class: #{obj.class} object_id: #{obj.object_id}" puts "Methods:", obj.methods.sort.inspect end Viewing Ruby documentation with ``ri`` ---------------------------------------- The ``ri`` command line documentation viewer is a very useful way to learn about Ruby modules, classes, and their methods. If it is not already installed, do this:: $ sudo gem install rdoc-data $ rdoc-data --install Then you should be able to display documentation by doing things like the following:: $ ri Array $ ri Array.each_with_index $ ri String.slice And, if the item for which you want documentation is in a module, try things like this:: $ ri Asciidoctor::Document $ ri Asciidoctor::Document.blocks Note: The above works on my machine because I have ``AsciiDoctor`` installed. ``AsciiDoctor`` is written in Ruby, by the way. And, if you want an interactive prompt with tab name completion, then run:: $ ri -i For more help with ``ri`` and ``rdoc``, see: http://jstorimer.com/ri.html Lexical matters ================= Lines ------- Ruby is line oriented. Typically you will write one Ruby statement per line. You *could* write more than one statement on a single line separated by semicolons, *but* it is considered poor form. A single statement on a line may end in a semicolon, but the semicolon is unnecessary, and bad style. A statement may continue across more than one line as long as the proceeding line (1) ends in an operator or (2) is an open context (inside "()", "[]", or "{}"), or (3) ends with a backslash as the last character. Names and tokens ------------------ Names in ruby are composed of letters, digits, underscores, and a few initial special characters. In some cases, those special characters determine the scope of the variable, as described in this table: ======================= =================================================== Initial character Variable Scope ======================= =================================================== $ A global variable [a-z] or _ A local variable @ An instance variable @@ A class variable (also global within a module) [A-Z] A constant ======================= =================================================== This table can be found at: http://www.techotopia.com/index.php/Ruby_Variable_Scope Predefined Ruby global variables ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ ============== ============================================================ Variable Name Variable Value ============== ============================================================ $@ The location of latest error $_ The string last read by gets $. The line number last read by interpreter $& The string last matched by regexp $~ The last regexp match, as an array of subexpressions $n The nth subexpression in the last match (same as $~[n]) $= The case-insensitivity flag $/ The input record separator $\ The output record separator $0 The name of the ruby script file currently executing $* The command line arguments used to invoke the script $$ The Ruby interpreter's process ID $? The exit status of last executed child process $stdin The standard input file $stdout The standard output file $stderr The standard error output file ============== ============================================================ This table can be found at: http://www.techotopia.com/index.php/Ruby_Variable_Scope A few more variables that are automatically available in your scripts: - The magic variable ``__FILE__``, which contains the name of the current file. - The variable ``ARGV``, which is a synonym for ``$*``. Also see a more extensive list of pre-defined variables at: http://en.wikibooks.org/wiki/Ruby_Programming/Syntax/Variables_and_Constants#Pre-defined_Variables Blocks and indentation ------------------------ The Ruby convention for indentation is 2 spaces per indentation level. Do *not* use hard tabs. Ruby code blocks are delimited by keywords such as ``if end``, ``do end``, etc. Curley brackets (``{`` and ``}``) can also be used to surround a block in some situations. Comments ---------- Everything to the right of a "#" character on any line is a comment. Block comments -- You can use Ruby's ``=begin`` and ``=end`` comment markers. For example:: =begin This comment spans several lines. =end Program structure ------------------- - Files - Modules - Classes - Functions - Structured statements (``if``, ``while``, etc) and blocks. A block is surrounded by ``do``-``end`` or by curly brackets. Structured statements contain nested blocks; they begin with a keyword (for example, ``if``, ``while``, ``until``, ``case``, etc.) and end with the keyword ``end``. Ruby does have a ``for`` statement, *but* you will usually use and iterator instead (one of the ``each_xxx`` methods. Operators ----------- You can find a summary of Ruby's operators here: `Ruby operators`_. And, the precedence of Ruby operators can be found here: `Ruby operator precedence`_. .. _`Ruby operators`: http://www.techotopia.com/index.php/Ruby_Operators .. _`Ruby operator precedence`: http://www.techotopia.com/index.php/Ruby_Operator_Precedence Note that the meaning of an individual operator may be different depending on the objects it is applied to. Code evaluation ----------------- When Ruby evaluates the code in a script file, it evaluates that code from the top of the file down to the bottom. Therefore, the order of code in that file may affect the behavior of the script. In particular, functions must be defined before they are called. Simple script structure ------------------------- Here is a simple script that can be run from the command line:: def main puts "hello" end main Notes: - The above script defines one method (``main``), then calls that function. Here is another, slightly less simple script that can be run from the command line *or* can be used in another script:: #!/usr/bin/env ruby # simple02.rb def print_upcase_message msg puts msg.upcase end def main print_upcase_message "aBcDeFgH" end if __FILE__ == $0 main end Notes: - The above script defines two methods: ``print_upcase_message`` and ``main``. - When run from the command line, the function main is called. - When used in another script via the ``require``, ``main`` is *not* called (until the script that uses it does so explicitly). Here is an example of the use of this script:: >> require './simple02.rb' ==>true >> print_upcase_message 'aaaBBBccc' AAABBBCCC ==>nil Built-in data-types ===================== There are *no* builtin datatypes in Ruby. Every data object is an instance of some class. So, for information on the equivalant of some of Python's builtin datatypes, see the documentation on the appropriate class, e.g. String, Integer, Array, File, etc. at: `Ruby-Doc.org -- http://ruby-doc.org/ `_. Many of these datatypes are in the Ruby core, which means that you do not need to use the ``require`` command in order to use them. Also, in order to get more information on a class or one of its methods, do at the command line:: $ ri String $ ri "String.scan" etc. And, inside the ruby interactive prompt, ``irb``, you can get a list of methods available for an object by typing:: >> some_obj.methods.sort Numeric types --------------- See these classes: - `Integer -- http://ruby-doc.org/core-2.0/Integer.html `_ - `Float -- http://ruby-doc.org/core-2.0/Float.html `_ - `Fixnum -- http://ruby-doc.org/core-2.0/Fixnum.html `_ Ruby numeric operators will automatically coerce integers to floats when you use mixed arithmetic. Example:: >> 3 / 6 ==>0 >> 3 / 6.0 ==>0.5 You can also explicitly convert an integer (or Fixnum) to a float. Example:: >> a = 5 ==>5 >> b = 3 ==>3 >> a / b ==>1 >> a.to_f / b ==>1.6666666666666667 Symbols (atoms) ----------------- Symbols are names in Ruby. They are called atoms in some other languages. Create a symbol using the colon for a literal representation. Symbols are singleton objects, which means that you can test to determine whether two symbols are the same symbol using an identity test (``.equal?``). You can also create them from strings. Examples:: >> a = :aaa ==>:aaa >> b = :aaa ==>:aaa >> a.equal? b ==>true >> a.object_id ==>430088 >> b.object_id ==>430088 You can also create symbols from strings. Example:: >> c = 'snowpea' ==>"snowpea" >> d = c.to_sym ==>:snowpea >> d ==>:snowpea >> d.class ==>Symbol >> 'oak tree'.to_sym ==>:"oak tree" Arrays -------- Information on class Array: - `Array -- http://ruby-doc.org/core-2.0/Array.html `_ Use square brackets and commas to give a literal representation of an Array and to create it. Use the ``length`` method to get the number of items in an Array. Example:: >> a = [11, 22, 33] ==>[11, 22, 33] >> a.class ==>Array >> a.length ==>3 Iteration -- Array.each etc ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ To iterate over the items in an Array, use one of the ``.each`` methods: - each - each_cons - each_entry - each_index - each_slice - each_with_index - each_with_object Example:: >> a1 = [111, 222, 333] ==>[111, 222, 333] >> a1.each { |x| puts "item: #{x}" } item: 111 item: 222 item: 333 ==>[111, 222, 333] >> a1.each_index { |idx, x| puts "#{idx}. item: #{x}" } 0. item: 1. item: 2. item: ==>[111, 222, 333] >> a1.each_with_index { |x, idx| puts "#{idx}. item: #{x}" } 0. item: 111 1. item: 222 2. item: 333 ==>[111, 222, 333] Index and select an item in an Array with square brackets and an integer. Negative numbers index from the right-hand side of the array. Example:: >> a = (1 .. 9).to_a ==>[1, 2, 3, 4, 5, 6, 7, 8, 9] >> a[0] ==>1 >> a[2] ==>3 >> a[-1] ==>9 >> a[-2] ==>8 Slice notation ~~~~~~~~~~~~~~~~ Ruby has a "slice" notation: - ``a[n, m]`` produces an Array of m elements starting at a[n]. - ``a[n .. m]`` produces an Array of the elements from a[n] to a[m], including a[m]. - ``a[n ... m]`` produces an Array of the elements from a[n] to a[m], excluding a[m]. Example:: >> a = (1 .. 9).to_a ==>[1, 2, 3, 4, 5, 6, 7, 8, 9] >> a[2, 3] ==>[3, 4, 5] >> a[2 .. 6] ==>[3, 4, 5, 6, 7] >> a[2 ... 6] ==>[3, 4, 5, 6] Test for membership in an ``Array`` with the ``.includes?`` method. Example:: >> a = [11, 22, 33] ==>[11, 22, 33] >> a.include? 22 ==>true >> a.include? 44 ==>false In order to use an ``Array`` as a stack, use its ``push`` and ``pop`` methods. Example:: >> pile = [] ==>[] >> pile.push 'apple' ==>["apple"] >> pile.push 'banana' ==>["apple", "banana"] >> pile ==>["apple", "banana"] >> pile.pop ==>"banana" >> pile ==>["apple"] The ``.push`` method can take multiple arguments. Instead of ``.push``, you can use the ``<<`` operator. Because the ``<<`` operator returns the array itself, it can be chained. Example:: >> container = [:peach, :nectarine] ==>[:peach, :nectarine] >> container << :apricot << :tangerine << :cherry ==>[:peach, :nectarine, :apricot, :tangerine, :cherry] >> container ==>[:peach, :nectarine, :apricot, :tangerine, :cherry] And, you can add the contents of an Array to another Array with the ``+=`` operator. Example:: >> a = [:aa, :bb] ==>[:aa, :bb] >> a += [:cc, :dd] ==>[:aa, :bb, :cc, :dd] >> a ==>[:aa, :bb, :cc, :dd] map, select, reject, reduce, etc ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Ruby also has some higher order functions that work on Arrays and on Enumerators, also. These include the following and their synonyms: - map and collect - select and find_all - reject - map and inject Here are a few of them: - ``an_array.collect { |item| block }`` -- Return an Array containing each object returned by block. Example:: >> a = [1, 2, 3, 4, 5, 6] ==>[1, 2, 3, 4, 5, 6] >> a.collect { |item| item * 3 } ==>[3, 6, 9, 12, 15, 18] - ``an_array.select { |item| block }`` -- Return an Array containing each item in ``an_array`` for which block returns true. Example:: >> a = [1, 2, 3, 4, 5, 6] ==>[1, 2, 3, 4, 5, 6] >> a.select { |item| item % 2 == 0 } ==>[2, 4, 6] - ``an_array.reject { |item| block }`` -- Return an Array containing each item in ``an_array`` for which block returns false. Example:: >> a = [1, 2, 3, 4, 5, 6] ==>[1, 2, 3, 4, 5, 6] >> a.reject { |item| item % 2 == 0 } ==>[1, 3, 5] - ``an_array.reduce(initial) { |accum, item| block }`` -- Return the result returned by block after applying it to ``accum`` and each ``item`` in the array. Example:: >> a = [1, 2, 3, 4, 5, 6] ==>[1, 2, 3, 4, 5, 6] >> a.reduce([]) { |accum, item| if item % 2 == 0 then accum.push item * 3 end; accum } ==>[6, 12, 18] Note that "inject" is another name for "reduce". Also note that there are forms of ``reduce`` without the arguments and without the block; see the Ruby documentation for more on that. - ``an_array.zip(other_array1, other_array2, ...)`` -- Return an Array of Arrays. The first item contains the first item from each initial arrays; the second item contains the second items; etc. Example:: >> a = [1, 2, 3] ==>[1, 2, 3] >> b = [11, 22, 33] ==>[11, 22, 33] >> c = [111, 222, 333] ==>[111, 222, 333] >> a.zip(b, c) ==>[[1, 11, 111], [2, 22, 222], [3, 33, 333]] Strings --------- - `String -- http://ruby-doc.org/core-2.0/String.html `_ Mutable -- Strings are mutable; they can be modified in place. Use ``my_string.methods.sort`` in irb, then look for methods that end with a "!". But, any individual string can be frozen so that it cannot be modified. For example:: >> desc = "A cute cat" ==>"A cute cat" >> desc ==>"A cute cat" >> # Change a slice (using a range) of the string. >> desc[2..5] = "big" ==>"big" >> desc ==>"A big cat" >> # Make the string immutable. >> desc.freeze ==>"A big cat" >> # Prove that we cannot modify a frozen string. >> desc[2..5] = "fast" RuntimeError: can't modify frozen String from (irb):47:in `[]=' from (irb):47 from /usr/bin/irb:12:in `
' Multi-byte characters ~~~~~~~~~~~~~~~~~~~~~~~ Ruby strings can contain multibyte characters. Here is an example:: city = "Sel\u00e7uk" ==>"Sel\u00E7uk" puts city Selçuk ==>nil city.each_char { |x| puts x } S e l ç u k ==>"Sel\u00E7uk" Strings are represented internally as a sequence of characters, not bytes. Those characters can contain unicode characters. Example:: >> puts "a\u011fb" ağb ==>nil >> puts "a\u011fb".length 3 ==>nil >> puts "a\u011fb"[0] a ==>nil >> puts "a\u011fb"[1] ğ ==>nil >> puts "a\u011fb"[2] b ==>nil Multibyte files -- For help with processing files that contain multibyte characters, see section `Multibyte encodings`_ in this document. String literals ~~~~~~~~~~~~~~~~~ Double quotes and single quotes can be used to define strings. Interpolation and backslash character escapes can be used within double quotes. Interpolation and backslash character escapes are treated literally inside single quotes. Example:: >> x = 3 ==>3 >> 'x: #{x}' ==>"x: \#{x}" >> "x: #{x}" ==>"x: 3" >> sprintf 'x: %s', x ==>"x: 3" >> '\t' ==>"\\t" >> '\t'.length ==>2 >> "\t" ==>"\t" >> "\t".length ==>1 Bachslash escape sequences ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Here is a table that describes them: ========= ============================================================ Escape Sequence Meaning ========= ============================================================ \\n newline (0x0a) \\s space (0x20) \\r carriage return (0x0d) \\t tab (0x09) \\v vertical tab (0x0b) \\f formfeed (0x0c) \\b backspace (0x08) \\a bell/alert (0x07) \\e escape (0x1b) \\nnn character with octal value nnn \\xnn character with hexadecimal value nn \\unnnn Unicode code point U+nnnn (Ruby 1.9 and later) \\cx control-x \\C-x control-x \\M-x meta-x \\M-\\C-x meta-control-x \\x character x itself (\\" a quote character, for example) ========= ============================================================ Interpolation ^^^^^^^^^^^^^^^ Use "#{expression}" within a double quoted string, where ``expression`` can be almost any ruby code. Example:: >> size = 35 ==>35 >> description = "pretty" ==>"pretty" >> "double size: #{size * 2}" ==>"double size: 70" >> "upper description: #{description.upcase}" ==>"upper description: PRETTY" The % notation ^^^^^^^^^^^^^^^^ Use % followed by a non-alpha-numeric character. This notation is especially useful when you want to include double quote and single quote characters in your string. Brackets are especially handy. Brackets can be contained within brackets if they balance. Example:: >> %[this contains "quote" characters, doesn't it] ==>"this contains \"quote\" characters, doesn't it" >> %[this on has [embedded] brackets] ==>"this on has [embedded] brackets" Modifiers for the % notation ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Here is a table of possible modifiers: ========= ================================================================== Modifier Meaning ========= ================================================================== %q[ ] Non-interpolated String (except for \\ \[ and \]) %Q[ ] Interpolated String (default) %r[ ] Interpolated Regexp (flags can appear after the closing delimiter) %i[ ] Non-interpolated Symbol (after Ruby 2.0) %I[ ] Interpolated Symbol (after Ruby 2.0) %w[ ] Non-interpolated Array of words, separated by whitespace %W[ ] Interpolated Array of words, separated by whitespace %x[ ] Interpolated shell command ========= ================================================================== The "here document" notation ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Use "<> long_msg = <"This is a long message of\nfluff fluff fluff fluff\nfluff fluff fluff fluff\nfluff fluff fluff fluff\nfluff fluff fluff fluff\n" >> puts long_msg This is a long message of fluff fluff fluff fluff fluff fluff fluff fluff fluff fluff fluff fluff fluff fluff fluff fluff ==>nil Interpolation is performed inside a "here document" string, except that if you use single quotes around the delimiter, it's not. And, you can "stack" multiple here documents:: string = [< ["the first thing\n", "the second thing\n", "and the third thing\n"] This example came from the Ruby Wiki. See that document for more on string literals: http://en.wikibooks.org/wiki/Ruby_Programming/Syntax/Literals#Strings sprintf string formatting and the % operator ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ ``sprintf`` provides capabilities similar to string interpolation with ``#{expr}``, but with more control over formatting. The ``%`` operator can be used instead of ``sprintf``. For example:: >> a = 5 ==>5 >> b = 'abcd' ==>"abcd" >> puts "%03d -- %s" % [a, b] 005 -- abcd ==>nil See the docs in the ``Kernel`` module: http://ruby-doc.org/core-2.0/Kernel.html#method-i-sprintf In particular, the formatting flags, type codes, width, and precision specification are described there. Here are several examples:: >> sprintf("%f %d", 1.23, 54) ==>"1.230000 54" >> sprintf "letter A: %x %d", 'A'.ord, 'A'.ord ==>"letter A: 41 65" >> sprintf "%.4f %g", 1.23, 300000 ==>"1.2300 300000" >> sprintf "%.4f %g", 1.23, 30000000000 ==>"1.2300 3e+10" >> "abcd".ljust 20 ==>"abcd " >> "abcd".rjust 20 ==>" abcd" >> "abcd".center 20 ==>" abcd " >> sprintf "%20s", "abcd" # right justify ==>" abcd" >> sprintf "%-20s", "abcd" # left justify ==>"abcd " String concatenation ~~~~~~~~~~~~~~~~~~~~~~ You can use ``+``, ``<<``, and ``.concat`` to concatenate strings. The ``<<`` and ``.concat`` operations modify a string in place, and are therefore faster than ``+`` when used to accumulate large strings. But, use string interpolation (``"...#{expr}..."``), rather than concatenation, when formatting a string in a single operation. Converting strings to arrays and arrays to strings ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ To convert an Array of strings into a single string, use the Array's ``.join`` method. Example:: >> a = ['the', 'bright', 'blue', 'balloon'] ==>["the", "bright", "blue", "balloon"] >> a.join ==>"thebrightblueballoon" >> a.join ' ' ==>"the bright blue balloon" >> a.join '||' ==>"the||bright||blue||balloon" To convert an Array of some other kind of objects into a single string, consider using the ``Array.collect`` method. For example:: >> b = [11, 22, 33, 44] ==>[11, 22, 33, 44] >> c = b.collect {|x| x.to_s } ==>["11", "22", "33", "44"] >> c.join "||" ==>"11||22||33||44" Although, the ``Array.join`` method seems to do some conversion of items to strings automatically, and even seems to handle Arrays nested inside Arrays. To convert a String into an Array, use the String's ``.split`` method. Example:: >> b = "the bright blue balloon" ==>"the bright blue balloon" >> b.split ==>["the", "bright", "blue", "balloon"] >> b.split(/\W/) ==>["the", "bright", "blue", "balloon"] >> b = "the bright\tblue balloon" ==>"the bright\tblue balloon" >> b.split ==>["the", "bright", "blue", "balloon"] >> b = 'abcd' ==>"abcd" >> b.split(//) ==>["a", "b", "c", "d"] >> >> # Use a regular expression to split on punctuation as well >> # as white space. >> data = "aaa bbb, ccc. \"ddd eee\" fff" ==>"aaa bbb, ccc. \"ddd eee\" fff" >> data.split /\W+/ ==>["aaa", "bbb", "ccc", "ddd", "eee", "fff"] Notes: - The default delimiter (separator) is white space, when no pattern is given. - You can use a regular expression to split on more complicated patterns. For example, "/\\W+/" will split off punctuation as well as white space. See example above. - Use the empty pattern (``//``) in order to split a string into individual characters. The pattern ``//`` matches between each character. For more on regular expressions, see: http://www.ruby-doc.org/core-2.1.2/Regexp.html. Or, if you have Ruby doc support, do ``$ ri regexp``. - But remember that you can process the characters in a String using its ``.each_char`` method and a block. So, in many cases, it will not be necessary to convert the String to an Array. Example:: >> b = 'abcd' ==>"abcd" >> b.each_char { |ch| puts "ch: #{ch}" } ch: a ch: b ch: c ch: d ==>"abcd" Hash -- dictionary -------------------- In Ruby, a dictionary is called a Hash. See: - `Hash -- http://ruby-doc.org/core-2.0/Hash.html `_ Here is the literal syntax for a hash:: >> table = {"peach" => "tasty", "necartine" => "tangy"} ==>{"peach"=>"tasty", "necartine"=>"tangy"} Or, if the keys are atoms, you can use this alternative literal syntax:: >> table = {peach: "tasty", nectarine: "tangy"} ==>{:peach=>"tasty", :nectarine=>"tangy"} You can mix these two syntaxes in the same literal expression:: >> table = {peach: "tasty", nectarine: "tangy", "tree" => "branchy"} ==>{:peach=>"tasty", :nectarine=>"tangy", "tree"=>"branchy"} >> table ==>{:peach=>"tasty", :nectarine=>"tangy", "tree"=>"branchy"} Then use square brackets to add, replace, and retrieve items:: >> # Add an item. >> table[:apricot] = "sweet" ==>"sweet" >> table ==>{:peach=>"tasty", :nectarine=>"tangy", :apricot=>"sweet"} >> # Replace an item. >> table[:peach] = "succulent" ==>"succulent" >> # Retrieve a value from the table. >> puts table[:nectarine] tangy ==>nil Remove a key/value pair from a hash -- Example:: >> table ==>{:peach=>"tasty", :nectarine=>"tangy", :banana=>"rich"} >> table.delete(:banana) ==>"rich" >> table ==>{:peach=>"tasty", :nectarine=>"tangy"} Check to determine whether a key is in the hash, use ``has_key?``, ``include?``, or ``member?``. They are all synonyms:: >> table ==>{:peach=>"tasty", :nectarine=>"tangy"} >> table.has_key? :banana ==>false >> table.has_key? :peach ==>true What can I use as a key in a Hash? -- You can use any object that responds to the method ``.hash``. However, ... *Caution:* If you add a value to a Hash using a mutable object (for example an Array) as the key in a hash and then you modifiy that object (for example, add an item to the Array), in some cases that mutable object can no longer be used to successfully access that value. Suggestion: if you intend to use an array as a key in a hash, consider using ``myarray.freeze`` to make that array immutable. You can also set a default value to be returned when you as for the value of a key that does not exist in the dictionary. If you do not explicitly set the default, then it is ``nil``. In other words, the default for the default is nil. Example:: >> table = Hash.new('nothing') ==>{} >> p table[:abc] "nothing" ==>"nothing" >> table.default = 'something' ==>"something" >> p table[:abc] "something" ==>"something" Files ------- Use class ``File``. See: - `File -- http://ruby-doc.org/core-2.0/File.html `_ ``File.open`` opens a file for read, write, append, etc access and creates a ``File`` object. Once you have opened a file (and created a File object), you can learn about it by typing the following at the ``irb`` interactive prompt:: > my_file = File.open('some_file.txt', "r") > my_file.methods.sort And, here is a piece of code that reads a text file and writes out each line capitalized:: def read_file(infilename) infile = File.open(infilename, "r") infile.each do |line| puts line.upcase end infile.close end read_file("test.txt") Notes: - A file object has a number of ".each" methods. You can learn about them with ``ri``. Or look here: - http://www.ruby-doc.org/core-2.0.0/File.html - http://www.ruby-doc.org/core-2.0.0/IO.html#method-i-each - In particular, there is an ``file_obj.each_with_index`` that gives an index number with each line. - Don't forget to close the file when you are finished, for example: ``file_obj.close``. And, the following example writes several lines to a text file:: def write_file outfilename data = [ "line 1", "line 2", "line 3", "line 4", ] outfile = File.open outfilename, "w" data.each_with_index {|item, idx| outfile.write "#{idx + 1}. #{item}\n" } outfile.close end write_file "tmp01.txt" Notes: - We need to explicitly add the new line character ("\\n") to the end of each line. - The method ``Array.each_with_index`` gives us an iterator that provides both the next item in the ``Array`` and an index (starting at 0) for each iteration. Standard out and standard err ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ You can write to ``stdout`` and ``stderr`` as follows:: $stdout.puts "This is a routine message" $stderr.puts "This is a error message" Standard input ~~~~~~~~~~~~~~~~ You can read the input that was "piped" to your program/script by using file ``$stdin``. Look for the various ``.read`` and ``.each`` methods. Here is a sample script that reads standard input (``$stdin``), converts text to upper case, and writes to standard output (``$stdout``):: def filter lines = $stdin.readlines lines.each do |line| line = line.upcase $stdout.write line end end filter If the above script is in a file ``filter.rb``, then you can run it as follows:: $ cat filter.rb | ruby filter.rb DEF FILTER LINES = $STDIN.READLINES LINES.EACH DO |LINE| LINE = LINE.UPCASE $STDOUT.WRITE LINE END END FILTER Multibyte encodings ~~~~~~~~~~~~~~~~~~~~~~ You will sometimes need to process files containing multibyte encodings. To do so, specify the encoding when you open the file. For example:: f = open("testdata01.txt", "r:utf-8") ==># f.each { |line| puts "line: #{line}" } line: aaa line: bbb line: çöğ line: ccc ==># f.close ==>nil To process individual characters (as opposed to bytes) look for methods ``.chars``, ``.each_chars``, etc in data read from the file or in the file object itself. Also note that ``my_str[n]`` indexes and selects *characters*, not *bytes*, and that ``my_str.length`` returns the number of *characters* in the string, not the number of *bytes*. Regular expressions --------------------- Define regular expressions with a pattern inside forward slashes. Example:: >> pattern1 = /x*(abc)y*(def)z*/ If forward slashes are not a convenient delimiter (for examle, because your pattern contains forward slashes, then you can create a regular expression as follows:: >> pattern2 = Regexp.new("x*(abc)y*(def)z*") Use a regular expression with any of these operators: - ``=~`` -- Returns the position of a match within the target if sucessful, else ``nil``. Use it to determine whether or not there is a successful match (``nil`` indicates failure) and to determine the location of the match in the target. Example:: >> "xxabbcyyydeeefzzzz" =~ /x*(ab*c)y*(de*f)z*/ ==>0 >> $& ==>"xxabbcyyydeeefzzzz" >> $~ ==># >> $1 ==>"abbc" >> $2 ==>"deeef" >> "aaabbbcccdddeee" =~ /ccc/ ==>6 Note the use of the special variables ``$&``, ``$~``, ``$1``, ``$2``, etc. to obtain the results of the last match. But, a better way is to use ``target.match(regexp)`` or ``regexp.match(target)``, which return a ``MatchData`` object. - ``regexp_pattern.match(target_string)`` -- Similar to the use of ``=~``, except that the return value is a ``MatchData`` object or ``nil`` if the match is not successful. Use it to extract the characters from the target that are matched by groups in the pattern. (Groups are surrounded by parentheses in the ``Regexp``.) Example:: >> /x*(ab*c)y*(de*f)z*/.match("xxabbcyyydeeefzzzz") ==># >> /x*(ab*c)y*(de*f)z*/.match("xxabbcyyydeeefzzzz").captures ==>["abbc", "deeef"] >> /x*(ab*c)y*(de*f)z*/.match("123") ==>nil >> md = "aaa111bbb222ccc".match(/[^\d]*(\d+)[^\d]*(\d+)/) ==># >> md.captures ==>["111", "222"] - ``!~`` -- Returns ``false`` if the match is successful, else ``true``. As a memory aid, remember that ``!=`` reverses the sense of ``==``. Example:: >> "111222333" !~ /222/ ==>false >> $& ==>"222" >> $~ ==># >> "111222333" !~ /555/ ==>true >> $& ==>nil ``MatchData`` objects -- Here are some things you can do with the ``MatchData`` objects that are returned by ``regexp_pattern.match(target_string)`` and that is the value of the ``$~`` special variable after a successful match:: >> mdobj = /x*(ab*c)y*(de*f)z*/.match("xxabbcyyydeeefzzzz") ==># >> mdobj ==># >> mdobj.class ==>MatchData >> mdobj.length ==>3 >> mdobj[0] ==>"xxabbcyyydeeefzzzz" >> mdobj[1] ==>"abbc" >> mdobj[2] ==>"deeef" >> mdobj.regexp ==>/x*(ab*c)y*(de*f)z*/ There are other objects that have methods that take a ``Regexp`` as an argument. ``String.match`` and ``String.scan`` are two useful ones. Here is a pattern that you can use to search a string for repeating occurances of a pattern and process each one:: $target1 = "111aabb222aaabbb333aaaabbbb444" $pat1 = /\d(a+)(b+)\d/ def test puts "pattern: #{$pat1}" puts "target: #{$target1}" pos = 0 while md = $target1.match($pat1, pos) puts "#{pos} #{md[0]} #{md[1]} #{md[2]}" pos = md.end(0) end puts "last pos: #{pos}" end test When run, this script displays the following:: $ ruby regexp_example.rb pattern: (?-mix:\d(a+)(b+)\d) target: 111aabb222aaabbb333aaaabbbb444 0 1aabb2 aa bb 8 2aaabbb3 aaa bbb 17 3aaaabbbb4 aaaa bbbb last pos: 28 Other built-in types ---------------------- Symbols ~~~~~~~ Symbols are marked with a leading colon, for example: ``:apple``, ``:red``, ``:monday``. Symbols stand for themselves. They are singletons. Two symbols that are spelled the same are guarantteed to be identical: they have the same object ID (returned by ``.object_id``). You can test for the identity of two symbols (whether they are the same symbol) with ``.equal?``. Example:: >> s1 = :tomato ==>:tomato >> s2 = :eggplant ==>:eggplant >> s3 = :tomato ==>:tomato >> s1.equal? s2 ==>false >> s1.equal? s3 ==>true Notes: - Symbols are often a good choice for use as keys in a Hash (dictionary). - Symbols are *not* garbage collected, whereas strings are. So, if your program needs a huge amount of symbols, consider the memory impact. The nil/None value/type ~~~~~~~~~~~~~~~~~~~~~~~~~ In Ruby, "None" is called "nil". It is an instance of class ``NilClass``. You can test for ``nil`` with either of the following:: > my_obj.nil? > not my_obj.nil? Boolean values ~~~~~~~~~~~~~~~~ Ruby's boolean values are ``true`` and ``false``. In the condition of Ruby statements like ``if``, ``while``, ``until``, etc, the values ``false`` and ``nil`` count as false; everything else is true. Ranges ~~~~~~~~ A range is an instance of class ``Range``. Ranges are defined with ``n .. m`` or ``n ... m`` (m >= n): - ``..`` includes the last item (m). - ``...`` excludes the last item (m). The end points n and m can be integers, floats, characters, strings, ... The end point objects must be comparable with ``<=>``. With *some* ranges, you can use ``range.to_a`` to produce an ``Array`` from a ``Range``. Test whether an object is "in" a ``Range`` with ``range.include? obj``. Use methods in the ``.each`` family to iterate over elements in a range. You can also create ranges with ``Range.new(start, end, exclusive=false)``. Example:: >> range1 = 2 .. 4 ==>2..4 >> range2 = 2 ... 4 ==>2...4 >> range1.to_a ==>[2, 3, 4] >> range2.to_a ==>[2, 3] >> range1.each { |item| puts "item: #{item}" } item: 2 item: 3 item: 4 ==>2..4 >> range3 = 4.5 .. 8.6 ==>4.5..8.6 >> range3.include? 7.1 ==>true >> range3.include? 10.8 ==>false Sets and frozensets ~~~~~~~~~~~~~~~~~~~~~ Sets are defined in a separate module. Use ``require`` to make it available. Here is an example:: >> require 'set' ==>true >> >> # Create a set of atoms. >> basket1 = Set.new [:apple, :banana, :nectarine, :peach, :watermelon] ==># >> # Add an item to the set. >> basket1.add :cantaloupe ==># >> basket2 = Set.new [:apple, :banana] ==># >> # Test whether a set contains an atom. >> basket2.include? :nectarine ==>false >> basket1.include? :nectarine ==>true >> basket2.add :apricot ==># >> # Get the union of two sets. >> basket1.union basket2 ==># >> # Check to see if basket1 was modified. >> basket1 ==># There is no separate frozen set class, but, you can freeze a set. Example:: >> require 'set' ==>true >> a = Set.new() ==># >> a.add(:apricot) ==># >> a.freeze ==># >> a.add(:boysenberry) RuntimeError: can't modify frozen Hash from /usr/lib/ruby/1.9.1/set.rb:228:in `[]=' from /usr/lib/ruby/1.9.1/set.rb:228:in `add' from (irb):80 from /usr/bin/irb:12:in `
' Functions and Classes -- A Preview ==================================== Define a function with ``def``/``end``. Parameters are optional. Use ``return`` to return a value, or list the value as the last value produced. For example, we can define a function in a file named ``tmp.rb``:: def greater(a, b) if a < b return false elsif a > b return true end false end And, then we use it in ``irb`` as follows:: >> load './tmp.rb' ==>true >> greater(3, 2) ==>true >> greater(4,4) ==>false >> greater(4,6) ==>false Statements ============ Assignment ------------ Ruby's assignment statement is very much like Python's. You can even do unpacking. For example:: >> a = ["abc", 123] ==>["abc", 123] >> b, c = a ==>["abc", 123] >> b ==>"abc" >> c ==>123 Ruby has "augmented" assignment operators: ============= =================== x += y x = x + y x -= y x = x - y x /= y x = x / y x \*= y x = x \* y x %= y x = x % y x \*\*= y x = x \*\* y ============= =================== Ruby can do parallel assignment and can swap values. Example:: >> # Parallel assignment. >> a, b, c = :apricot, :banana, :cherry ==>[:apricot, :banana, :cherry] >> puts a, b, c apricot banana cherry ==>nil >> # Swap the values in a and b. >> a, b = b, a ==>[:banana, :apricot] >> puts a, b, c banana apricot cherry ==>nil Note that the values on the right hand side (the Rvalue) are evaluated completely *before* the variables on the left hand side (the Lvalue) are modified. require and require_relative ------------------------------ Load external modules that you want to use in your Ruby script with the ``require`` method. (``require`` is a method in the ``Kernel`` module). Example -- Suppose file ``node_lib.rb`` contains this:: #!/usr/bin/env ruby module NodeLib class Node o o o Then you can load it and use the ``Node`` class in it as follows:: require "node_lib" node1 = NodeLib::Node.new(...) Notes: - Reference items in a module using the module name, for example: ``ModuleName::Item``. - Items define outside of a ``module`` block become global in the script that uses ``require``. - ``load`` works like ``require``, except that ``load`` forces the module to be loaded even if it has been previously loaded. - The list of directories to be searched for modules is in global variable ``$:``. You can add additional directories with something like this:: >> $:.push "." >> $:.push "/usr/home/myself/ruby_modules" Although, that is a questionable thing to do. - Better, in most situations, is to add the directory containing the code you want to reference with ``require`` to the ``RUBYLIB`` environment variable. See the section on `Modules`_ elsewhere in this document. - Use ``require_relative`` instead of ``require`` to ask Ruby to search for modules in directories relative to the location where execution is taking place. - ``require`` evaluates the code in a file. So if you have code in that file which you do not want to be evaluated when loaded with ``require``, but you do want evaluated when the script is run, then hide that code inside this:: if __FILE__ == $0 # Code to be run when script is executed but not when "required". o o o end puts and print ---------------- ``puts`` and ``print`` are not actually statements. They are functions in the ``Kernel`` module. Use ``puts`` to display information. It's a method in ``Kernel``. ``puts`` automatically adds a newline. If you do not want the newline added, then use ``$stdout.write``. Example:: $stdout.write "You say goodbye" $stdout.write "I say hello" Or, use ``print`` to write out text without a newline automatically added:: >> a = 3; puts "a: "; puts a a: 3 ==>nil >> a = 3; print "a: "; puts a a: 3 ==>nil if elif else ----------------- Here is a sample ``if`` statement:: def greater(a, b) if a < b return false elsif a > b return true else return false end end puts "3 < 4: #{greater(3, 4)}" puts "4 < 3: #{greater(4, 3)}" puts "4 < 4: #{greater(4, 4)}" Running this file (if_statement.rb) produces the following:: $ ruby tmp.rb 3 < 4: false 4 < 3: true 4 < 4: false Notes: - There can be multiple ``elsif`` clauses. They are all optional. - The else clause is optional. - The condition that follows the ``if`` and ``elsif`` keywords can be any expression. - The values ``false`` and ``nil`` count as false; every other value counts as true. - Add keyword ``then`` after the condition, if you want a statement following the condition on the same line as the condition. Example:: >> name = "Dave" ==>"Dave" >> if name == "Dave" then puts "Hello" end Hello ==>nil But, usually, this is considered bad form. - A condition can be any expression, that is anything that returns a value. - The logical operators are ``not``, ``and`` (or ``&&``), and ``or`` (or ``||``). - Use parentheses to override the precedence of operators, or when you feel that parentheses help clarify the order of evaluation. - Parentheses can also be used to continue a long condition across multiple lines. unless statement ~~~~~~~~~~~~~~~~~~ An ``unless`` statement is like an ``if`` statement with the sense of the test reversed. You can think of "unless ..." being equivalent to "if not ...". Example:: >> flag = true ==>true >> value = if flag then 100 else 0 end ==>100 >> value ==>100 >> flag = false ==>false >> value = if flag then 100 else 0 end ==>0 >> value = unless flag then 100 else 0 end ==>100 >> value = if not flag then 100 else 0 end ==>100 True and false values in conditions ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ In a condition (for example, in an ``if``, ``unless``, and ``while`` statement), the values ``false`` and ``nil`` count as false; every other value counts as true. In other words, ``false`` and ``nil`` cause a condition to fail; everything else causes it to succeed. if expression ~~~~~~~~~~~~~~~ The ``if`` statement is itself an expression. In Ruby, statements return a value and are, therefore, expressions. However, when only the value is needed, you may find it more convenient to use an ``if`` expression. The form of an ``if`` expression is as follows:: ? : Examples:: >> a = 4 ==>4 >> b = 6 ==>6 >> a < b ? -1 : +1 ==>-1 >> b < a ? -1 : +1 ==>1 if and unless modifier ~~~~~~~~~~~~~~~~~~~~~~~~ You can also use an ``if`` expression as a modifier on a statement. Example:: >> flag = true ==>true >> puts "hello" if flag hello ==>nil >> flag = false ==>false >> puts "hello" if flag ==>nil >> puts "hello" unless flag hello ==>nil Operators for conditions ~~~~~~~~~~~~~~~~~~~~~~~~~~ You can learn about the precedence of all Ruby operators here: `Ruby operator precedence`_. Ruby operators (highest to lowest precedence) -- Operators with a "Yes" in the method column are actually methods, and as such may be overridden: +--------+------------------------+--------------------------------------+ | Method | Operator | Description | +========+========================+======================================+ | Yes | [ ] [ ]= | Element reference, element set | +--------+------------------------+--------------------------------------+ | Yes | \*\* | Exponentiation (raise to the power) | +--------+------------------------+--------------------------------------+ | | | Not, complement, unary plus and | | Yes | ! ~ + - | minus (method names for the last two | | | | are +@ and -@) | +--------+------------------------+--------------------------------------+ | Yes | \* / % | Multiply, divide, and modulo | +--------+------------------------+--------------------------------------+ | Yes | \+ \- | Addition and subtraction | +--------+------------------------+--------------------------------------+ | Yes | >> << | Right and left bitwise shift | +--------+------------------------+--------------------------------------+ | Yes | & | Bitwise AND | +--------+------------------------+--------------------------------------+ | Yes | ^ | | Bitwise exclusive OR and regular | | | | OR | +--------+------------------------+--------------------------------------+ | Yes | <= < > >= | Comparison operators | +--------+------------------------+--------------------------------------+ | | | Equality and pattern match operators | | Yes | <=> == === != =~ !~ | (!= and !~ may not be defined as | | | | methods) | +--------+------------------------+--------------------------------------+ | | && | Logical AND | +--------+------------------------+--------------------------------------+ | | || | Logical OR | +--------+------------------------+--------------------------------------+ | | .. ... | Range (inclusive and exclusive) | +--------+------------------------+--------------------------------------+ | | ? : | Ternary if-then-else | +--------+------------------------+--------------------------------------+ | | = \%= \{ /= | Assignment | | | -= += \|= \&= | | | | >>= <<= | | | | \*= &&= ||= \*\*= | | +--------+------------------------+--------------------------------------+ | | defined? | Check if specified symbol defined | +--------+------------------------+--------------------------------------+ | | not | Logical negation | +--------+------------------------+--------------------------------------+ | | or and | Logical composition | +--------+------------------------+--------------------------------------+ | | if unless while until | Expression modifiers | +--------+------------------------+--------------------------------------+ | | begin/end | Block expression | +------------------------------------------------------------------------+ for statement --------------- Here is an example of a ``for`` statement:: def for_statement container = [111, 222, 333] for item in container do puts "item: #{item}" end end for_statement If this code is in file ``for_statement.rb`` and you run it, you will see:: $ ruby for_statement.rb item: 111 item: 222 item: 333 Notes: - Advice -- Do not use the ``for`` statement. Look for a way to use an enumerator. Usually, this means looking for one of the methods in the ``each`` family, e.g. ``.each``, ``.each_with_index``, etc. For example:: >> container = [111, 222, 333] ==>[111, 222, 333] >> container.each do |item| >> puts "item: #{item}" end item: 111 item: 222 item: 333 ==>[111, 222, 333] - The ``do`` keyword in the ``for`` statement is optional unless you want to put the statement in the block on the same line, which is bad form except in ``irb``. - The ``for`` statement will also do unpacking. Example:: >> for x, idx in container.each_with_index do puts "idx: #{idx} x: #{x}" end idx: 0 x: 111 idx: 1 x: 222 idx: 2 x: 333 ==>[111, 222, 333] But, again, the above is likely better re-written as:: >> container.each_with_index do |x, idx| puts "idx: #{idx} x: #{x}" end idx: 0 x: 111 idx: 1 x: 222 idx: 2 x: 333 ==>[111, 222, 333] - Scope: - The variables defined and used in the block passed to an enumerator are local to the block. - The variables created by a ``for`` statement are local to the enclosing block. while statement ----------------- The ``while`` statement repeats a block as long as a condition is true. Example:: def test idx = 0 while idx < 4 puts "idx: #{idx}" idx += 1 end end test If we put this in a file (e.g. ``while_statement.rb``), and run it, we'll see:: $ ruby while_statement.rb idx: 0 idx: 1 idx: 2 idx: 3 But ``while`` statements are often more clearly written using an enumerator. This example is roughly equivalent to the above ``while`` statement:: >> (0 .. 3).each { |item| puts "item: #{item}" } item: 0 item: 1 item: 2 item: 3 ==>0..3 until statement ----------------- Ruby also had an ``until`` statement, which is similar to the ``while`` statement, except: (1) the keyword ``until`` is used in place of ``while``; and (2) the sense of the condition is reversed (the block is evaluated while the condition is *not* true. This can sometimes simplify writing the condition. Example:: def test3 idx = 0 until idx > 8 idx += 1 if idx.modulo(2) == 0 next end puts "idx: #{idx}" end end break and next ---------------- The ``break`` statement exits the immediately enclosing loop. Example:: def test2 (3 .. 10).each do |item| if item >= 7 break end puts "item: #{item}" end end test2 If we put this in a file and run it, we'll see:: $ ruby tmp2.rb item: 3 item: 4 item: 5 item: 6 The ``next`` statement starts execution at the top of the loop. Example:: def test1 idx = 0 while idx < 8 idx += 1 if idx.modulo(2) == 0 next end puts "idx: #{idx}" end end test1 If we put this in a file and run it, we'll see:: $ ruby tmp2.rb idx: 1 idx: 3 idx: 5 idx: 7 The ``redo`` statement starts execution at the top of the loop, but *without* evaluating the condition or fetching the next element. Exceptions -- begin-rescue-end -------------------------------- Here is a sample script that: 1. Defines several exception classes. The are subclasses class Exception. 2. Implements a function (``f1``) that calls another funtion (``f2``) that raises an exception. 3. Calls function ``f1`` and handles the exception that might be raised. :: class ModerateException < Exception end class ExtremeException < Exception end def f1 value f2 value end def f2 value case value when "bad" raise ModerateException, "things are bad" when "verybad" raise ExtremeException, "things are very bad" end return "every thing is ok" end def test arg1 result = "undecided" begin result = f1 arg1 rescue ModerateException => msg puts "handling moderate exception. msg: \"#{msg}\"" rescue ExtremeException => msg puts "handling extreme exception. msg: \"#{msg}\"" end puts "result: \"#{result}\"" end if __FILE__ == $0 if $*.length != 1 $stderr.puts "usage: ruby exceptions.rb