class: center # Ruby Basics ![Ruby](img/ruby-logo.png) [http://pjb3.me/bewd-ruby-basics](http://pjb3.me/bewd-ruby-basics) .footnote[ created with [remark](http://github.com/gnab/remark) ] --- # Hello, World! Create a file called `hello.rb` and put the following code into it: ```ruby puts "Hello, World!" ``` This is a ruby program, albeit a very small one. To run a ruby program, from the command line, you pass the name of the file containing the program that you want Ruby to run for you, like this: .terminal $ ruby hello.rb Hello, World! As you can see, the Ruby program printed out the string `Hello, World!` --- # Interactive Ruby Ruby provides an interactive shell called **IRB**. In IRB, you can enter small pieces of Ruby code and see what it evaluates to. Open the file '~/.irbrc` using Atom: .terminal atom ~/.irbrc and copy and paste the code from [this gist](https://gist.github.com/pjb3/9166610) into that file and save it. Now you can launch IRB like this: .terminal $ irb >> The `>>` prompt indicates you are now in the IRB shell instead of Bash. Use the IRB shell to follow along with the examples in these slides. When you type in a command and press enter, IRB will show you the result starting with a `=>` --- # Objects All values in Ruby are Objects. The type of an object is determined by its class. ## Strings .terminal >> "Hello World" => "Hello World" >> "Hello World".class => String ## Integers .terminal >> 42 => 42 >> 42.class => Fixnum ## Floating Point Numbers .terminal >> 3.14159 => 3.14159 >> 3.14159.class => Float There are lots more we will learn... --- # Expressions Expressions evaluate to an Object ## Mathematical Operators .terminal >> 41 + 1 => 42 >> 43 - 1 => 42 >> 6 * 7 => 42 >> 210 / 5 => 42 >> 42 % 10 => 2 --- # Expressions ## Boolean Operators Equality .terminal >> 42 == 42 => true >> 42 == 21 * 2 => true >> 42 != 10 => true Different types of objects are not equal .terminal >> 42 == "42" => false Comparison >> 5 > 2 => true >> 6 <= 5 => false --- # true and false Boolean expressions return an Object that is either `true` or `false`. Each of these objects have their own class: .terminal >> true.class => TrueClass >> false.class => FalseClass These two objects are both commonly referred to as booleans even though they don't have the same Class. --- # nil Another basic type is `nil`, which also has it's own class `NilClass`: .terminal >> nil.class => NilClass `nil` is the value in Ruby that represents "nothing" or "no value". Even though it represents "nothing" or "no value", it still is an Object. --- # Expressions ## String Operators The `+` operator concatenates strings together into a new string .terminal >> "Hello" + " " + "World" => "Hello World" The `<<` operator appends to a string .terminal >> "Hello" << " World" => "Hello World" Interpolation allows you to include the result of evaluating an expression in a String .terminal >> "40 + 2 = #{40 + 2}" => "40 + 2 = 42" If you don't want interpolation to occur, use single quotes or precede the `#` with `\` .terminal >> '40 + 2 = #{40 + 2}' => "40 + 2 = \#{40 + 2}" --- # String Formatting Another operator you can use with String is the [format operator][format], which is the `%` character. You use the format operator like this: .terminal >> "%.2f" % 9.9 => "9.90" In this example, the string on the left side of the `%` is treated as a format specification, which has special characters that start with a `%` in the string. On the right side of the `%` are the values you want used in the formatted string that is the result of the operation. In this case, we are saying that we want to have a floating point number always formatted with 2 decimal points. You can have multiple values interpolated into a format specification by supplying an Array, like this: .terminal >> "%-20s $%.2f" % ['Soap', 0.9] => "Soap $0.90" In this example, we are saying we want the string printed and then padded with empty spaces up to 20 characters, and then a dollar sign followed by the floating point number with 2 decimal places. There is quite a bit you can do with the format, you can read more about in [the Ruby docs for the sprintf methods][sprintf]. The formatting operator is used extensively in programs that will output text in the command line. Since almost everyone uses a [fixed-width font][fixed-width] in a terminal, the spacing can be used to produce alignment to make the output more readable. [format]: http://www.ruby-doc.org/core-2.1.0/String.html#method-i-25 [sprintf]: http://www.ruby-doc.org/core-2.1.0/Kernel.html#method-i-sprintf [fixed-width]: http://en.wikipedia.org/wiki/Monospaced_font --- # Variables Variables are named referenced to objects. You create a variable by assigning an Object to it. .terminal >> my_name = "Paul" => "Paul" >> my_name => "Paul" >> my_name == "Paul" => true >> my_name == 42 => false Shorthand for incrementing a variable .terminal >> i = 0 => 0 >> i += 1 => 1 Equivalent to .terminal >> i = i + 1 => 2 --- # Dynamic Typing Ruby is said to have "dynamic typing" because you can assign any type of Object to the same variable .terminal >> x = "hello" => "hello" >> x = 42 => 42 >> x = 3.14159 => 3.14159 In other some other languages (C/Java), you must declare what type of objects can be assigned to a variable. A compiler will check This is known as "static typing". ```java String x = "hello"; x = "world"; x = 42; ``` .terminal $ javac Example.java Example.java:5: incompatible types found : int required: java.lang.String x = 42; ^ 1 error --- # Arrays Arrays are Objects that are an ordered collection of Objects .terminal >> [42,99,100] => [42,99,100] >> some_numbers = [42,99,100] => [42,99,100] >> some_numbers == [42,99,100] => true Arrays are indexed starting with 0. Think of the index as the offset from the first element. .terminal >> some_numbers[0] => 42 >> some_numbers[2] => 100 You can add elements to an Array .terminal >> some_numbers << 2001 => [42, 99, 100, 2001] >> some_numbers[3] => 2001 --- # Array Values An Array can hold values of any type of Object and they don't have to be of the same type: .terminal >> a = [1, "Hello", nil, false] => [1, "Hello", nil, false] Because an Array can contain any Object, it can contain other Arrays, as well as more complex types that we learn about later like Symbols, Hashes, Objects, Classes, etc: .terminal >> [[1,2], :foo, { x: 1, y: 2}, Object.new, Object] => [[1, 2], :foo, {:x=>1, :y=>2}, #
, Object] --- # Objects can change You can assign new objects to a variable .terminal >> my_name = "John" => "John" Some operators modify the object .terminal >> my_name << " Doe" => "John Doe" >> my_name => "John Doe" Some operators return a new object .terminal >> my_name = "John" => "John" >> my_name + " Doe" => "John Doe" >> my_name => "John" --- # Multiple variables can refer to the same object If the object changes, since these variables are referring to the same object, you see the change in both variables .terminal >> my_name = "John" => "John" >> name = my_name => "John" >> my_name << " Doe" => "John Doe" >> name => "John Doe" ![Diagram](img/reference_diagram.png) --- # Constants Constants begin with a capital letter and are variables that you don't intend to change .terminal >> NAME = "Paul" => "Paul" You will get a warning if you change a constant .terminal >> NAME = "John" (irb):5: warning: already initialized constant NAME (irb):4: warning: previous definition of NAME was here The Object the constant refers to can change .terminal => "John" >> NAME << " Doe" => "John Doe" >> NAME => "John Doe" Although that is not typical --- # When to use constants Constants are good to use when you have a value that doesn't ever change and you might use in multiple places in your program. Even if you are only using it in one place, it's good to give a name to what the value represents, rather than using a [magic number][magic-number]. For example, you could write this method like this: ```ruby def circumference(radius) 2 * 3.141592653589793 * radius end ``` But it would better to do this: ```ruby PI = 3.141592653589793 def circumference(radius) 2 * PI * radius end ``` FYI, for this particular constant, Ruby already has it defined for you in the constant [Math::PI][pi]. [magic-number]: http://en.wikipedia.org/wiki/Magic_number_(programming)#Unnamed_numerical_constants [pi]: http://www.ruby-doc.org/core-2.1.0/Math.html#PI --- # Symbols Used for labeling things in code .terminal >> :paul => :paul >> :paul.class => Symbol Symbols are similar to Strings .terminal >> my_name = :paul => :paul >> "Hello #{my_name}" => "Hello paul" but Symbols are not the same type as Strings, so they are not equal to Strings .terminal >> :paul == "paul" => false --- # Symbols vs. Strings The concept of Symbols vs. Strings is confusing to most people when first learnign Ruby. Another way to think of it is to think of Integers vs. Strings. As we said earlier, values must be of the same type to be considered equal: .terminal >> 42 == 42 => true >> 42 == "42" => false The Integer forty-two is equal to the Integer forty-two, but the Integer forty-two is not equal to a string with the digits 4 and 2. Symbols and Strings have a similar relationship in that they are a like in a lot of ways, but still not the same class and therefore not equal. This can be confusing sometimes because you might have something like this, where you have a variable `name` that is declared elsewhere in the program: .terminal >> "Hello #{name}" => "Hello paul" >> name == "paul" => false And you are expecting it to be a String, but it is actually a Symbol. Check the class of the Object to determine if that is the case: .terminal >> name.class => Symbol --- # When to use a Symbol instead of a String Once you understand the difference between Symbols and Strings, the next question you might have would be when should I use a Symbol instead of a String? Once you understand more about the Ruby language, it will be easier to explain the difference. For now, just understand that there is a difference and be able to recognize in code examples that `:this` is a Symbol and `"this"` is a String and they are not the same thing. If feel you are ready for a deep dive into this subject, try reading [this article](http://www.troubleshooters.com/codecorn/ruby/symbols.htm), but just being able to distinguish between the two is enough when you are getting started building Rails app. I suggest bookmarking that article for further reading after we've covered Ruby in more detail. --- # Hashes A collection of objects named with keys .terminal >> { :name => "Paul", :age => 35 } => {:name=>"Paul", :age=>35} More concise Syntax new in Ruby 1.9+. You can only use this syntax when you want the keys to be Symbols: .terminal >> { name: "Paul", age: 35 } => {:name=>"Paul", :age=>35} You can use Strings or any other object for the keys, but Symbols are most common .terminal >> { "name" => "Paul", "age" => 35 } => {"name"=>"Paul", "age"=>35} --- # Hash Operators >> person = { name: "Paul", age: 35 } => {:name=>"Paul", :age=>35} Get the value for a key >> person[:name] => "Paul" Assign a value to a key >> person[:city] = "Baltimore" => "Baltimore" >> person => {:name=>"Paul", :age=>35, :city=>"Baltimore"} If there is no key, you get nil .terminal >> person[:email] => nil >> x = nil => nil >> x.class => NilClass --- # Symbol keys vs. Strings keys Since Symbols aren't equal to Strings, you can't get an object from a Hash using a Symbol if the keys are Strings: .terminal >> person = { name: "Paul" } => {:name=>"Paul"} >> person[:name] => "Paul" >> person["name"] => nil or vice-versa, if the keys are Strings, you can't access the values using Symbols: .terminal >> person = { "name" => "Paul" } => {"name"=>"Paul"} >> person["name"] => "Paul" >> person[:name] => nil --- # Regular Expressions Regular expressions are patterns. You can use them to see if a String matches a Regular Expression: .terminal >> /oo/.class => Regexp >> "food" =~ /oo/ => 1 >> "food" =~ /^foo/ => 0 >> "food" =~ /d$/ => 3 >> "food" =~ /foo$/ => nil >> "food" !~ /foo$/ => true Other mainstream programming languages, such as Java, PHP, JavaScript, Python, Perl and more, also have Regular Expressions. Regular Expressions can get insanely complex. For an example, look at this [regular expression for validating an email address](http://stackoverflow.com/a/719543). To learn more about Regular Expressions, check out [Rubular](http://www.rubular.com/), especial the cheat sheat at the bottom. You should try to commit those parts of regular expressions to memory. --- # Basic Ruby Types ```ruby # String "Hello, World!" # Integer 42 # Float 3.14159 # Boolean true # Symbol :name # Regexp /\d/ # Array [42,99,100] # Hash { :name => "Paul", :age => 35 } # nil nil ``` --- # Conditional Statements `if [condition] then [then expression] else [else expression] end` .terminal >> x = 42 => 42 >> if x > 40 then 1 else 0 end => 1 The `else` is optional .terminal >> if x > 40 then 1 end => 1 `unless` is the opposite of `if` .terminal >> unless x > 40 then 1 end => nil The conditional can be at the end of the line .terminal >> 1 unless x > 40 => nil --- # Truthiness `nil` and `false` are considered to be "falsy", everything else is "truthy" .terminal >> x = 42 => 42 >> if x then "yes" else "no" end => "yes" >> x = 0 => 0 >> if x then "yes" else "no" end => "yes" >> x = "false" => "false" >> if x then 1 else 0 end => 1 >> x = false => false >> if x then 1 else 0 end => 0 >> x = nil => nil >> if x then 1 else 0 end => 0 --- # Methods All Objects have methods. You can call a method with `object.method` .terminal >> "hello".upcase => "HELLO" Methods can have arguments .terminal >> 5.remainder(2) => 1 The methods that an object has are determined by what type of Object it is # Methods Are Messages .terminal >> "hello".send(:upcase) => "HELLO" >> 5.send(:remainder, 2) => 1 Every method call is really sending a message to an Object --- # Object Methods Determine what type of Object it is .terminal >> :hello.class => Symbol >> nil.class => NilClass Ask an object if it is a a certain type .terminal >> :hello.is_a?(Symbol) => true >> :hello.is_a?(Object) => true >> :hello.is_a?(String) => false Methods that end in a question mark are usually methods that return `true` or `false` as the answer to a "question" --- # Object Methods Get the string representation of an Object .terminal >> :hello.to_s => "hello" >> nil.to_s => "" Get a more detailed representation of an Object for debugging .terminal >> :hello.inspect => ":hello" >> nil.inspect => "nil" --- # Object Methods Create a copy of the Object .terminal >> my_name = "John" => "John" >> name = my_name.dup => "John" >> my_name << " Doe" => "John Doe" The original Object has been modified .terminal >> my_name => "John Doe" The copy has not .terminal >> name => "John" --- # Object Methods Freeze an object so it cannot be changed .terminal >> name.freeze => "John" >> name << " Doe" RuntimeError: can't modify frozen String from (irb):38 from /Users/pbarry/.rbenv/versions/2.0.0-p0/bin/irb:12:in `
' --- # String Methods Change the case of characters in a String .terminal >> "hello".capitalize >> "Hello" >> "hello".upcase => "HELLO" >> "hello".upcase.downcase => "hello" Split a String into an Array .terminal >> "hello word".split => ["hello", "world"] >> "hello, world!".split(',') => ["hello", " world!"] Remove whitespace from the beginning and the end .terminal >> " hello ".strip => "hello" --- # String Methods Replace parts of a String .terminal >> "hello world".sub('hello','goodbye') => "goodbye world" Replace all occurrences of a String .terminal >> "one two one".sub('one','three') => "three two one" >> "one two one".gsub('one','three') => "three two three" Get a "slice" of a String .terminal >> "hello world".slice(0) => "h" >> "hello world".slice(-1) => "d" >> "hello world".slice(4,4) => "o wo" --- # String Methods Convert to an Integer .terminal >> "42".to_i => 42 Convert to a Float .terminal >> "3.14159".to_f => 3.14159 Convert to a Symbol .terminal >> "hello".to_sym => :hello --- # Numeric Methods Check for even/odd .terminal >> 1.even? => false >> 1.odd? => true Get the absolute value of a number .terminal >> -42.abs => 42 Round a decimal .terminal >> 3.14159.round => 3 >> 3.14159.round(2) => 3.14 --- # Array Methods .terminal >> a = [42,99,100] => [42, 99, 100] Determine how many elements are in the Array .terminal >> a.size => 3 >> a.length => 3 Join an Array into a String separated by a character .terminal >> a.join(',') => "42,99,100" Get the first/last elements of an array .terminal >> a.first => 42 >> a.last => 100 --- # Array Methods Add/remove an element from the end of the Array .terminal >> a.push 120 => [42, 99, 100, 120] >> a.pop => 120 >> a => [42, 99, 100] Add/remove an element from the front of the Array .terminal >> a.unshift 1 => [1, 42, 99, 100] >> a.shift => 1 >> a => [42, 99, 100] --- # Array Methods Remove an element from the Array .terminal => a = [42, 99, 100] >> a.delete 100 => 100 >> a => [42, 99] Find the index of an element in the Array .terminal >> a.index(99) => 1 Remove an element from the Array by index .terminal >> a.delete_at(1) => 99 >> a => [42] --- # Hash Methods .terminal >> person = { name: "Paul", age: 35 } => {:name=>"Paul", :age=>35} Get the keys of the Hash .terminal >> person.keys => [:name, :age] Get the values of the Hash .terminal >> person.values => ["Paul", 35] --- # Hash Methods Merge hashes .terminal >> person.merge(name: "Paul Barry", city: "Baltimore") => {:name=>"Paul Barry", :age=>35, :city=>"Baltimore"} `merge` returns a new Hash, does not modify the Hash >> person => {:name=>"Paul", :age=>35} `merge!` modifies the Hash >> person.merge!(name: "Paul Barry", city: "Baltimore") => {:name=>"Paul Barry", :age=>35, :city=>"Baltimore"} >> person => {:name=>"Paul Barry", :age=>35, :city=>"Baltimore"} Having two versions of a method, one that ends without an exclamation mark and does not modify the Object and another that ends with an exclamation mark that does modify the Object, is a common idiom --- # Writing your own methods Like any programming language, Ruby allows you to define your own methods. Methods are usually more than one line, so at this point in the tutorial, we're going to switch over to save our code into a file and running the program. Open a new Ruby file named anything you want, `code.rb`, for example, in Atom: .terminal $ atom code.rb Put the following code in the file: ```ruby puts RUBY_VERSION ``` And run it from the bash command line (not IRB), like this: .terminal $ ruby code.rb 2.1.0 More conveniently, you can run the program from Atom. Install the package called "Script" in Atom > Preferences > Packages, then once you have saved the file, you can press command-i to run the program from within Atom (use your right hand). The output will look like this: .terminal 2.1.0 You will see this output in a small window at the bottom of Atom. You can press ESC to close that output window. --- # Defining Methods You define a method using the **def** keyword, like this: ```ruby def say_hello puts "Hello Paul" end ``` The name of the method follows def. When you write the above code, the method is not executed, it is only defined, saved for later under the name `say_hello`. To call the method, put a few more lines of code in the same file, below the `end` that corresponds to the `def`, like this: ```ruby say_hello ``` When you run the program, the output will be: .terminal Hello Paul --- # Method Arguments Methods can have one or more arguments. You specify a name for each argument your method will accept in parentheses after the method name. The argument is then available as a variable that is equal to the value you pass in when you call the method while the body of the method is executed. ```ruby def say_hello(name) puts "Hello #{name}" end ``` Put a few more lines of code in the same file to call the method that you defined three times, each time with a different value for the argument: ```ruby say_hello "Peter" say_hello "Paul" say_hello "Mary" ``` The output will be: .terminal Hello Peter Hello Paul Hello Mary --- # Multiple Arguments Methods can have more than one argument, each one separated by a comma: ```ruby def multiply(x, y) x * y end ``` .terminal >> multiply(6, 7) => 42 When calling a method, you can omit the parentheses: .terminal >> multiply 6, 7 => 42 The most common Ruby style is to omit the parentheses if there are no arguments to the method and include them when there are. There are exceptions to the rule, where calling the method without parentheses is considered the preferred style, we'll learn about some of those cases as we learn more about Ruby. --- # Optional Arguments You can have optional arguments to your method. The optional arguments come after the required arguments and must have a default value specified: ```ruby def multiply(x, y = 1) x * y end ``` This method can be called with 1 or 2 arguments: .terminal >> multiply(6) => 6 >> multiply(6, 7) => 42 --- # Default Argument Evaluation The value of a default argument can be an expression, like calling another method. The expression is evaluated at the time you call the method, not at the time the method is defined: ```ruby def current_time(time = Time.now) puts "It is #{time}" end ``` Each time the method is called, the default value for time is recalculated: .terminal >> current_time It is 2014-02-27 09:19:32 -0500 => nil >> current_time It is 2014-02-27 09:19:36 -0500 --- # Variable Arguments ```ruby def multiply(*args) args.reduce(:*) end ``` When you put a `*` in front of the name of an argument, that means that all the arguments are put into an Array. Don't worry about the body of the method for now, just know that it multiplies each of the values in the `args` Array together. .terminal >> multiply(2) => 2 >> multiply(2, 3) => 6 >> multiply(2, 3, 4) => 24 >> multiply(2, 3, 4, 5) => 120 --- # Keyword Arguments Ruby 2.0 introduced a feature called keyword arguments. This allows you to give each possible argument a name and a default value. When you call a method like this, you can specify the arguments in any order and leave ones out: ```ruby def chapter(title, number: 1, body: nil) puts "Chapter #{number}: #{title}" puts body if body end ``` .terminal >> chapter "The beginning" Chapter 1: The beginning => nil >> chapter "The next chapter", number: 2 Chapter 2: The next chapter => nil >> chapter "A long one", number: 3, body: "A long long time ago..." Chapter 3: A long one A long long time ago... => nil For a more in-depth explanation of this topic, check out this blog post: [http://dev.af83.com/2013/02/18/ruby-2-0-keyword-arguments.html](http://dev.af83.com/2013/02/18/ruby-2-0-keyword-arguments.html) --- # Local Variables An import concept to understand about variables is called **scoping**, which refers to the context which a variable is available. Put this code into your program: ```ruby def say_hello(name) puts "Hello #{name}" end say_hello "Paul" puts name ``` Run the program from the bash command line instead of from within Atom and the output will look like this: .small[ .terminal $ ruby code.rb Hello Paul code.rb:6:in `
': undefined local variable or method `name' for main:Object (NameError) ] Before we go over local variables and scoping, let's go over how to read error messages like this. _(you can run this program from within Atom and you will also get an error, but the output is a little more confusing, so I suggest running the program from the command line while learning about exceptions and exception handling)_ --- # Exceptions The error on the previous slide looked like this: .small[ .terminal $ ruby code.rb Hello Paul code.rb:6:in `
': undefined local variable or method `name' for main:Object (NameError) ] After the `say_hello` method is successfully called, the next line **raises an exception**. When that happens, the name of the file and the line number of where the exception occurred is the first thing printed out in the error message. From this we can tell the exception is on line 6 of `code.rb`. After that is the exception message, which in this case is: .terminal `
': undefined local variable or method `name' for main:Object These messages can be helpful for debugging the error. Before we discuss this specific error, let's go over errors in a little more detail. The last part of the error message tells us which class the exception is. In this case it is a `NameError`. --- # Unhandled Exceptions Sometimes in programs we might have to call code where an exception could be raised. Imagine trying to download something from the Internet via Ruby code. If the network is down, the code will raise an exception. The way this code is written right now, this error is referred to as an **unhandled exception**. When you have an unhandled exception in your program, your program stops (a.k.a crashes). In this example, if we have more code after line 6, it would never be reached, because the program would crash. Let's add some code to demonstrate that: ```ruby def say_hello(name) puts "Hello #{name}" end say_hello "Paul" puts name say_hello "Peter" ``` When you run this, you will get the same error and "`Hello Peter`" will not be in the output, because the exception is on line 6, so that program crashes at the point and line 7 is never reached. --- # Handling Exception If you don't want your program to crash when there is an Exception, you can use the `begin` and `rescue` keywords, like this: ```ruby def say_hello(name) puts "Hello #{name}" end say_hello "Paul" begin puts name rescue NameError => ex puts "oops, there was a #{ex.class} exception" end say_hello "Peter" ``` The output will be: .terminal Hello Paul oops, there was a NameError exception Hello Peter The `begin` keyword start a block of code that will be executed. If a NameError is raised in the begin block, the program will assign the exception to the variable `ex`, execute the contents of the rescue block and then continue executing the rest of the program. If a different class of Exception is raised in the begin block, the raise block will not be executed and the exception will be treated as an unhandled exception, meaning your program will crash. --- # Back to local variables So now that we know what happens when your program encounters an error, let's talk about why there is an error in this case. Let's restore the program back the original example that caused the error: ```ruby def say_hello(name) puts "Hello #{name}" end say_hello "Paul" puts name ``` When you follow the execution path of this code, you might think that in the first 3 lines, the `say_hello` method is defined, then when we call `say_hello` on line 5, the variable name is set to `"Paul"`, so on the next line, we should be able to refer to the name variable, right? No, what actually happens is that variables defined inside a method, including the ones defined as arguments, are called **local variables**. When the method starts executing, the `name` variable is set to `"Paul"` and it remains set while the `say_hello` method is executing, but once the method is done being executed the variable goes **out of scope**, meaning the variable is no longer defined, which is why the `NameError` exception is raised. You can think of local variables as variables that temporarily exist while the method is being executed, but cease to exist once the method is finished being executed. --- # Stack Traces Try entering this program and running it from the command line: ```ruby def foo puts oops end def bar foo end bar ``` The output will look like this: .small[ .terminal $ ruby test.rb test.rb:2:in `foo': undefined local variable or method `oops' for main:Object (NameError) from test.rb:6:in `bar' from test.rb:9:in `
' ] The error message is multiple lines. The actual error is on line 2, but the following lines in the error method tell us how we got to the point where the exception occurred. The way to read this is that the error occurred on line 2 in the `foo` method, which was called on line 6 from the `bar` method, which was called on line 9 from the main part of the program. These kinds of error messages are called **stack traces** and are invaluable when debugging larger programs. --- # Executing code multiple times What should you do if you want to call the `say_hello` for 3 different, or 10 different names? You could just add multiple lines of code, like this: ```ruby def say_hello(name) puts "Hello #{name}" end say_hello "Peter" say_hello "Paul" say_hello "Mary" ``` This will work, as you would expect, but there is a better way to do this --- # Iterating over elements in an Array If we put each of names we want to say hello to in an Array, we can loop through each element of the Array and pass the element to the `say_hello` method, like this: ```ruby def say_hello(name) puts "Hello #{name}" end people = ["Peter", "Paul", "Mary"] for person in people say_hello person end ``` This will print the same output as the previous slide, but what happens here is that we create the Array with each of the names and then we use the `for` keyword to create a loop. `for` will assign the value in the first element of the array in the variable `people` to a **local variable** called `person` and then execute the body of the for loop. In the body of the for loop, we call the `say_hello` method, passing the value of `person`, which is `"Peter"`, as the argument. After the body is executed once, the next value in the array, `"Paul"`, is assigned to `person` and the body of the for loop is executed again. This process repeats until the body of the for loop has been executed with the last element of the Array. For loops and methods are similar in that they both have local variables and a body that is executed with those local variables set. The difference is that the body of a method is only executed once for each time you call it, whereas the body of a for loop is executed once for each element of the Array. --- # Updating the Array Using a for loop instead of multiple calls to the method is better because the code will work with any number of elements in the Array. Without a loop, we have to know ahead of time exactly how many names there will be and the program can only work with exactly those values. Here's an example where we add another element to the array ```ruby def say_hello(name) puts "Hello, #{name}!" end people = ["Peter", "Paul", "Mary"] people << "Bob" for person in people say_hello person end ``` Now all 4 names are printed. You can imagine in a more complex program we might have to get the names from a database or some external source. In that case, the number of the names will change while our program is running, so we need the flexibility of a for loop. --- # Working with multiple files As your Ruby programs get bigger, you will want to organize them into separate files. You might have some code that is not specific to your project and could be used in other projects and you might want to have a way to package up that code and share it. Ruby has mechanisms for this. First, create a file called `my_lib.rb` with a method in it: ```ruby def my_method puts "This is my method!" end ``` Next, create a second file called `my_program.rb` in the same directory as `my_lib.rb` with the following code in it: ```ruby require './my_lib' my_method ``` When you run `my_program.rb`, first it loads the file `./my_lib.rb` into your program. It's really the same thing as if you just copy and pasted the code from the file `my_lib.rb` into `my_program.rb`. `require` keeps track of the files that have been loaded and doesn't load them twice, so it's safe to require the same file in multiple places throughout your program. --- # Requiring features from the standard library Ruby comes with a lot of code that you can use in your own programs. For example, if you want to format data as JSON, you can use the JSON module from the standard library by requiring it. ```ruby require 'json' data = { name: 'Paul', city: 'Baltimore' } puts data.to_json ``` --- # Where Ruby looks for files to load You might have noticed in the last example that we didn't specify the full or relative path to the file `json`, but Ruby knew how to load it. Ruby does this in a similar way to how bash finds programs. There is a variable called `$LOAD_PATH` that has an Array of directories that Ruby will search through to find files to load. My load path looks like this: .small[ .terminal >> puts $LOAD_PATH /Users/paul/.rbenv/plugins/rbenv-gem-rehash /Users/paul/.rbenv/versions/2.1.0/lib/ruby/gems/2.1.0/gems/hirb-0.7.1/lib /Users/paul/.rbenv/versions/2.1.0/lib/ruby/site_ruby/2.1.0 /Users/paul/.rbenv/versions/2.1.0/lib/ruby/site_ruby/2.1.0/x86_64-darwin13.0 /Users/paul/.rbenv/versions/2.1.0/lib/ruby/site_ruby /Users/paul/.rbenv/versions/2.1.0/lib/ruby/vendor_ruby/2.1.0 /Users/paul/.rbenv/versions/2.1.0/lib/ruby/vendor_ruby/2.1.0/x86_64-darwin13.0 /Users/paul/.rbenv/versions/2.1.0/lib/ruby/vendor_ruby /Users/paul/.rbenv/versions/2.1.0/lib/ruby/2.1.0 /Users/paul/.rbenv/versions/2.1.0/lib/ruby/2.1.0/x86_64-darwin13.0 ] You can modify the load path by adding values to tell Ruby about new places where it should look for code. You can also use the `-I` switch when launching Ruby to specify additional directories that should be on the load path: .terminal $ irb -I foo => nil >> puts $LOAD_PATH.first /Users/paul/dev/back-end-web-development/foo --- # Rubygems Ruby comes with a program called `gem` that allows you to install other libraries outside of the standard library that you can load into your program. You can run the command `gem list` to see all of the gems that you currently have installed. If you already have rails installed, you will see a list of quite a few gems. If you don't have Rails installed yet, [follow the instructions here](https://github.com/pjb3/back-end-web-development#things-to-install) to do so. You can search for and learn about gems at [http://rubygems.org](http://rubygems.org). You don't have to do anything special to load up the ruby gems framework, it is integrated into ruby. When you try to load a gem, ruby adds that gem to the load path for you. For example, start up irb and then look at the load path. Then do `require 'rack'`. Once you have done so, you will see the directory where the rack gem is installed as been added to your load path: .small[ .terminal >> require 'rack' => true >> puts $LOAD_PATH /Users/paul/.rbenv/plugins/rbenv-gem-rehash /Users/paul/.rbenv/versions/2.1.0/lib/ruby/gems/2.1.0/gems/hirb-0.7.1/lib /Users/paul/.rbenv/versions/2.1.0/lib/ruby/gems/2.1.0/gems/rack-1.5.2/lib /Users/paul/.rbenv/versions/2.1.0/lib/ruby/site_ruby/2.1.0 /Users/paul/.rbenv/versions/2.1.0/lib/ruby/site_ruby/2.1.0/x86_64-darwin13.0 /Users/paul/.rbenv/versions/2.1.0/lib/ruby/site_ruby /Users/paul/.rbenv/versions/2.1.0/lib/ruby/vendor_ruby/2.1.0 /Users/paul/.rbenv/versions/2.1.0/lib/ruby/vendor_ruby/2.1.0/x86_64-darwin13.0 /Users/paul/.rbenv/versions/2.1.0/lib/ruby/vendor_ruby /Users/paul/.rbenv/versions/2.1.0/lib/ruby/2.1.0 /Users/paul/.rbenv/versions/2.1.0/lib/ruby/2.1.0/x86_64-darwin13.0 ] --- # That's All For Now In this tutorial you've learned: * The basic ruby types * How to call methods * What some of the built-in methods are * How to define your own methods * What happens when when your program encounters an error * How to iterate over Arrays with a for loop * How to load code into your program * What Rubygems are and how to use them Tune in next time to learn about Object-Orientation and Blocks!