Skip to content

Instantly share code, notes, and snippets.

@Sujimichi
Created December 27, 2010 12:20
Show Gist options
  • Save Sujimichi/756096 to your computer and use it in GitHub Desktop.
Save Sujimichi/756096 to your computer and use it in GitHub Desktop.
also available here http://pastie.org/pastes/1408409
##Ruby Tutorial
#This tutorial is to help you get started with using Ruby.
#I Assume you have Ruby installed with a copy of 'irb'. If not then install RubyVersionManager and ruby 1.9.2 (instructs to follow)
#Starting with the most basic, this is a comment. Marked with the # symbol
#I will use #=> to mean the output of a command
#The Basics
1 #This is a number, more specifically a Fixnum. An Integer value without decimal.
#You can do Maths with Fixnums as expected;
1 + 3 * 3 - 1 #=> 9
(1 + 3) * (3 - 1) #=> 8
5 / 2 #=> 2 !!! Wait thats not right should be 2.5.
#Calculations involving just Fixnums will return a Fixnum. Fixnum / Fixnum => Fixnum.
#When you need a decimal answer use a decimal in the calculation
5.0 / 2 #=> 2.5 #which is a Float
#5 / 2.0 would have the same effect (also see type casting later)
#A Fixnum in a calculation with a Float will return a Float.
#paste this into your irb window;
puts "hello World!!"
#That HAS to be the easiest implementation of that age old intro to programming
"hello World!!" #Is a String
#puts is a ruby method which outputs strings to the command line
"anything inside double quotes"
'or single quotes is a string object'
"you can put 'single quotes' inside double without breaking the string" #Double quotes are more "powerful"
"you can echo values/variables/method calls into a string like this #{5 + 2}" #The contents of the #{} is called at the time is parsed
#Ruby also has a short string class called Symbol
:a_symbol #will be treated like the string "a_symbol" (but it is not a string)
#Symbols can not have spaces and are prefixed with :
#Defining and Calling Variables
#Variables do not have to be pre defined
my_variable = 4.2 #you just assign a variable a value with the = operator (see naming conventions later)
my_variable #=> 4.2 #And call it
#A variable takes on the properties of the object Class you placed in it, therefore;
my_variable.round #=> 4 because it is now a FLoat and you can call .round on a Float
#the Class of variables is dynamically reassigned
var1 = 42 #var1 is now a Fixnum
var1 = "a string" #var1 is now a String
#There is no problem reusing a already defined variable for a different Class
a = 8039849
b = 119.5
c = 2893638431 #Assign some values to some variables
((a * b * 2) + c).round #Do Some maths
#=> 4815162342 #Return answer (which happens to be the number from Lost!)
#Float, Fixnum Symbol and String are all different types of Class.
#An instance of a Class e.g; 5 is an object. More on this later, just note that;
#Everything in Ruby is an object, so everything has a Class.
#Type Casting - Changing one class into another
2.to_f #=> 2.0 It has cast the Fixnum into a float
#So you can write 5.0/2 as
5.to_f / 2 #=> 2.5
#You can cast a Float to a Fixnum
2.8.to_i #=> 2 It has cast the Float into a Fixnum, note it is rounded down
:ruby.to_s #=> "ruby" The symbol has been cast to a string
#There are limits to what can be cast into what. For example an Array [] cannot be cast into an Integer, but a string can;
"this string is not a number".to_i #=> 0
"5 might get converted thou".to_i #=> 5 #A number at the beginning of a string is parsed, the rest is ignored.
#Rounding
2.8.round #=> 3 Will also return a Fixnum this time rounded according to decimal value
2.8.floor #=> 2 #round up
2.4.ceil #=> 3 #round down
##Equality and Comparison
true #Ruby's yin and yang
false #true and false are the boolean operators in Ruby, both are objects so yep they have a Class too.
5 == 5 #=> true
5 == 4 #=> false #Basic equality test
# == can also be written with .eql? which is a method on the object 5
5.eql?(4) #=> false
true != false #=> true #The ! is the not operator in Ruby; true does not equal false returns true :)
not true == false #=> true #not is synonymous with !
#greater than less than
5 > 4 #=> true
5 > 5 #=> false
5 < 5 #=> false
5 >= 5 #=> true
5 <= 5 #=> true
##Arrays and Hashes
#Arrays are indicated with square brackets []
a = [] #A new empty array object (of the class Array)
a = Array.new #Would have the same effect
#Arrays are linear stores for arbitrary objects. Arrays retain their order
an_array = [1, 2.5, 3, "some str", true]
an_array.first #=> 1
an_array.last #=> true #first and last, methods on the array object
an_array[1] #=> 2.5 #Accessing by index using []
#NOTE Array indexes start at 0 !!!
an_array_of_arrays = [ [1,2,3], ["a", "b", "c"] ] #two arrays inside another array
an_array_of_arrays[0][1] #=> 2 #the 2nd value of the 1st inner array
an_array_of_arrays[1][2] #=> "c" #the 3rd value of the 2nd inner array
#Hashes are indicated with curly brackets {}
h = {} #a new empty hash object (do I need to say, class == Hash) {} == Hash.new
#hashes are indexable stores for arbitrary objects. Hashes do not retain order, they store a key, value pair
h = {:a_key => "a string", :true => true, :some_data => [1,2,3]}
#values in hashes are addressed like arrays, only the keys is used rather than numerical index
h[:some_data] #=> [1,2,3]
#Both Arrays and Hashes can be written over several lines to improve readability
an_array_of_arrays = [
[1,2,3],
["a", "b", "c"],
"foo"
]
hash = {
:a_key => "a string",
:true => true,
:some_data => [1,2,3]
}
##Logic
#The Good ol' If block
a = (rand * 10).round #rand returns a Float between 0 and 1
if a.eql?(1)
puts "the value a is 1"
elsif a.eql?(2)
puts "the value a is 2"
else
puts "Im a really stupid program that can only identify 1 and 2"
end# puts "the value of a is #{a}" #Would be a better!
#Ruby's unless block
#Ruby syntax has a nicer way of writing if not true or if !true
unless a.eql?(0) #Same as if !a.eql?(0) OR if not a.eql?(0) OR if a != 0
puts "the value of a is #{a}"
else
puts "a was zero"
end
#Ruby allows 'if / unless' to be written inline without an 'end'. If there was no need to report "a was zero" then;
puts "the value of a is #{a}" unless a.eql?(0)
#AND OR && ||
puts "the value of a is #{a}" unless a.eql?(0) || a.is_a?(String) # || is the OR operator
#This will now only run if a is not a String or if a is not == to 0
#is_a? is an equality test for class same as value.class == String
puts "the value of a is #{a}" if a.is_a?(Fixnum) && a >= 1 # && is the AND operator
#&& is a "fast failing" AND. If the first assertion fails it returns without evaluating the others.
#If b && a; And b is more computationally intensive it would be better to write if a && b. Thus if a is false it does not need to call b
#In this case, if a is not a Fixnum then a >= 1 will not be evaluated.
##Loops
#Loops in Ruby come in various different forms.
#To call some code n number of times. The code in between the do..end is called a block.
5.times do |i|
puts i
end
#will loop 5 times. In each loop the variable i will be available; starting with i = 0 and ending on i=4
#It will however return 5 at the end (explanation later)
#loop through an array
values = [1,2,3]
for value in values #Now I've shown you this loop, I want you to forget it an never use it!!
puts value
end
values.each do |value| #This how you 'should' write a loop to iterate through an array.
puts value
end
#The variable defined within the | | pipes is in the scope of the loop only. Once the loop as finished you could not call value
#They are both valid syntax but the first one is just a wrapper for the later.
#The first one makes a nice sentence 'for thing in things' but is not interchangeable with any other Ruby loop syntax.
#The latter is more akin to other aspects of Ruby (see methods and blocks later)
#That same loop could also be written
values.each {|value| puts value } #the do..end are replaced with {..}
#Variable scopes and Loops
#Certain variables are available both inside and outside a loop, others are limited to just withitn the loop.
s = 0
values.each{|v|
s = s + v #The variable s is in scope both inside and outside the loop, v is just inside the loop.
} #note the { } notation is not limited to one line usage.
s #=> 6 #After the loop, 's', defined before the loop is in scope.
#v is not in scope anymore (see more about scope later)
v #=> NameError: undefined local variable or method `v' for main:Object
#At the end of an each loop you will be returned the object which was iterated over. After array.each{|i| #somecode } you are returned array.
#This allows you to chain method calls. So in a contrived example where you want to do two loops over the same object one after the other you could say;
values.each{|value|
puts "the first pass outputs this string with the value: #{value}" #block for the first loop
}.each{|value|
puts "the 2nd pass outputs this string with the value: #{value + 50}" #block for the 2nd loop
}
#IT would also work just fine to write it like this
values.each do |value|
#some code
end.each do |value|
#some more code
end
#But *I think* thats Ugly, buts that's a code style / readability question.
#Ruby offers different ways to do comparable things so you can use the best one for the given job.
#Ruby has a number of variations on the lowly loop.
values.each_with_index do |value, i| #Two values are passed into the block, the element and its index (starting with 0)
puts "the value in the #{i}th position is: #{value}"
end
values = [1,2,3]
s = values.inject{|val, next_val| val + next_val } #.inject is also a loop like each but with a twist.
#On the first pass val will contain the 1st element and next_val the 2nd.
#However on subsequent passes val will contain the result of the previous pass, next_val will have the next element.
#In this case (over [1,2,3]) on the first pass val and next val are 1 and 2. the blocks' result is their addition so on the next pass val is 3 and next_val is 3.
#On the final pass val is 6 and next_val is nil as no further elements exist so the loop ends returning 6
s #=> 6
#Perhaps two of the most useful forms of loop in Ruby are .map and .select
changed_values = values.map{|v| v + 1 }
changed_values #=> [2,3,4] #the result of each pass is returned as a element of the resulting array (of same size)
vals = ["foo", 7, 1, 23, "bar", 3, 5, 9]
vals.map{|v| v.is_a?(Fixnum) } #=> [false, true, true, true, false, true, true, true]
vals.map{|v| v if v.is_a?(Fixnum) } #=> [nil, 7, 1, 23, nil, 3, 5, 9]
vals.select{|v| v.is_a?(Fixnum) } #=> [7, 1, 23, 3, 5, 9]
#select is like the previous map only the 'v if' is implicit AND it removes the nil values.
#select selects elements for which the block evaluates true
#Don't forget with all these examples using {} notation for the blocks that the 'do...end' notation would work just as well.
##Methods or Functions
#A method is a named block of code encased in a def..end
def a_simple_method
#This method can be called but will return 'nil'
end
def add_values a, b
a + b
end
#The above method takes two arguments and sums them. It must have both arguments satisfied.
#Ruby will implicitly return result of the last line in a method.
#Methods are called like this
result = add_values(2, 8) #calls with args and assigns result to variable
results = add_values 3, 5 #method calls can be written without the parenthesis, but some consider that sloppy. Depends on usage realy.
#Now lets change the method to make the last arg optional. if b is not supplied then it is set to be nil
def add_values a, b = nil
if b.nil? #Any object can have .nil? called on it. It is inherent to all classes. In fact it is inherited (see Class inheritance later)
return a #return halts the method or a loop block it is within and returns the value at that line.
end
a + b
end
#This method will return a is b is not given, or if both given will add them
#Rubys inline 'if' would allow this to be written like this;
def add_values a, b = nil
return a if b.nil?
a + b
end
#NOTE nil is not the lack of an object, nil is an object itself. It can not be used to test if something exists
foo.nil? #=> NameError: undefined local variable or method `foo' for main:Object
foo = nil
foo.nil? #=> true
#While nil is not the same as false, it is considered false in an if context.
#The previous method could be written 'return a if !b' or 'return a if not b' or better still use 'unless'
def add_values a, b = nil
return a unless b
a + b
end
#This would perhaps be the best way to write this, assuming the most of the time you do supply both arguments
def add_values a, b = nil
return a + b if b
a
end
#Now for a different method
def sum_values values = Array.new #Takes one argument or sets it to [] if values not supplied
return nil unless values.is_a?(Array) #don't do anything and return nil unless values is an Array
values.inject{|i,j| i + j } #use inject to sum the elements in values and return implicitly
end
#method calls, map, inject select etc all return objects which can be treated as usual.
#So you can call methods one after the other sequentially
values = [1,2,3]
values.map{|v| v + 2}.inject{|i,j| i + j}.floor.to_f #=> 12.0
sum_values(values) * 2 #=> 12
#So now you have the basics of Ruby variables, methods, logic, loops and some primitive Classes
#You can define a method
def sum_values values = Array.new
values.inject{|i,j| i + j } if values.is_a?(Array) #same effect as above sum_values, just more compact
end
#and assign data to a variable
vals = [34, 53.87, 12.2, rand*10, 34.4]
#And pass the variable into the method to get a result
answer = sum_values(vals)
#But this all seems rather functional. Isn't Ruby meant to be Object Oriented? Yes, but you can write in both an OO and functional style.
##Rubys Naming Conventions
#Ruby comes from japan where they are sick of tryingToRead horribleCammelCased varaiableNames likeTheses
#Instead in_ruby we_use_underscored variable_and_method_names
#This is the convention;
Array #Capitalization is used for Classes and Constants
Array.new #All methods are lowercase and underscored
an_array = Array.new #An instance of a class (an object) is lowercase_underscored
##Object Oriented - Classes
#In Ruby every thing is an object and every object has a class. Also classes in Ruby are hierarchically arranged.
#So far you have seen some of the basic classes like String and Array. On every class you can instantiate a new instance of that class
a = Array.new
s = String.new
#on any object you can inquire about its class.
a.class #=> Array
s.class #=> String
5.class #=> Fixnum
a.class.class #=> Class
#The Class of a class is Class!! wtf.
#This is where you can see that everything in Ruby is a Class, and also that every class is a decedent of Class.
#Well great, what does that all mean?
#The inheritance of classes means that all classes have the methods which are defined in Class.
#for example you can call .nil? on any object because .nil? is defined on Class.
#So if Class is itself a class, can I do this?
Class.new
#Answer yes, but all you get back is a Class so its not much use.
#To define a new class is like defining a method only the keyword is class not def and the name Must be CapitalisedCammelCased
class Vegetable
end
#And there you have it, Vegetable is now defined.
vegetable = Vegetable.new
vegetable.class #=> Vegetable
#But still not a very useful class. We want to have vegetables which do things.
class Vegetable
def initialize weight # initialize is called when .new is called on a Class. Vegetable.new will call this method
@weight = weight.to_f # @variables!! A variable with @ is in the scope of the class, not just the method. @weight can be accessed anywhere inside a vegetable
end
def weighs
puts @weight
end
end
#Now we could create some vegetables with weight values and get the values back
veg_1 = Vegetable.new(5)
veg_2 = Vegetable.new(8)
veg_1.weighs #=> 5
veg_2.weighs #=> 8
#OK now we want some different types of vegetable and this is where we use class inheritance
class Tomato < Vegetable
def good_for_throwing?
@weight >= 8 # returns the boolean response from >=
end
end
class Potato < Vegetable
def poisonous?
rand < 0.1
end
end
tomato = Tomato.new(9)
potato = Potato.new(5)
[tomato, potato].map{|veg| veg.weighs} #=> [9,5] #map the weights from each veg
tomato.good_for_throwing? #=> true
potato.poisonous? #=> rand dependent output
#We now have two classes which share some common methods but which behave differently.
#You can now create groups of objects of these classes.
#Ruby allows classes to be redefined at runtime. This means you can use an existing class, but modify it during execution.
#Assuming that you pasted the above code defining Vegetable and its sub classes into your irb window, paste this in.
class Vegetable
def eat_a_bit bit = rand
return "none left" if @weight <= 0
@weight -= bit
end
end
#The Existing Vegetable class has now been patched with a new method. You can now call .eat_a_bit on any vegetable.
potato.eat_a_bit
#Going back to the earlier example of using a method to sum values in an array, we can now put that method into Array itself
vals = [34, 53.87, 12.2, rand*10, 34.4]
#Currently calling vals.sum will not work. There is no .sum method. So we need to define one.
class Array
def sum
self.inject{|i,j| i + j } #self is a references to the instance of that class. It has replaced the call to values from sum_values before.
end
end
#And now there is!!
#The code you add will replace existing methods of the same name but leaves the rest unchanged.
vals.sum #=> 6
#Now you have the basics of creating a class and extending existing classes. With the definition of methods is all there really is to it.
##Advanced Stuff
#When you define a method it is a block of code which has a name. When you use a loop the block of code inside the loop is nameless.
#There is another nameless block which is simple called a 'block' and uses &blk to represent it.
#You can define a method with takes &blk as an argument. It can only take one &blk and it must be the last arg.
def test r = rand, &blk
yield(r)
end
#This method takes some code passed into it as &blk and 'yields' that code inside the method
#it also passes r into the block with it is called (r is rand is not given)
test { puts "I don't take an arg" }
test {|f| puts "I take an arg and show it #{f} inside this string" }
ans = test {|f| 5 * 3 + f }
#When passing args as well as a block the syntax to call is like this; method(variables){block}
ans = test(6){|f| 5 * 3 + f }
#This is not just useful. This is fundamental to how Ruby works.
#When you call values.each{|f| #some_code} you are calling the method .each and passing it a &blk.
#In all cases where a {} or do end encases some code, that is a &blk which will be yield at some point.
#.each on an array will call yield on the block you passed for each element in the array and will pass the element in as the arg.
#This is why the array.each{|element| #code} syntax is more regarded than the for element in array syntax.
#Another form of nameless method is a Proc. These are like blocks but they can be assigned to variables.
some_code = Proc.new{|arg|
if arg.is_a?(String)
puts "arg is a string"
else
arg * 5
end
}
#some_code now holds a bit of code which can be called. In this case because there is an arg it must be supplied.
some_code.call("slkjflksdjf") #=> arg is a string
some_code.call(5) #=> 25
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment