The Ruby Book
Step by Logical Step
By Nicholas Johnson
version 0.9.0 - Beta

By Nicholas Johnson
version 0.9.0 - Beta
Hello and Welcome to this super exciting little introduction to Ruby, the language that aims to make developers fall in love with programming again.
Ruby is an expressive, open language that lets you do an awful lot in next to no time at all.
It's the language that powers half of the world's tech startups. Why? Because it lets small teams of crack developers build incredible applications that would have taken months, or even years using traditional techniques.
And what's in it for you? Ruby is a friendly community. People want to help you, people want to pay it forwards. And once you're good, you can earn very significant money. A good Ruby developer in London will earn around £500 per day. This is a language which is worth your time.
Wow your clients with your awesome productivity. Tackle side projects with ambition and alacrity.
Here are some of the great things about Ruby:
…And more nice little touches than you can shake a stick at.
Now are you excited? You're about to learn a language, designed specifically to make programmers happy, that doesn't make you jump through hoops, that gives you the respect you deserve, and which, if you master it, can make you rich. Welcome to Ruby!
To get started you're going to need some basics. I'm going to assume here that you have a copy of Ruby, a terminal, and a text editor of some description. You'll need access to irb, the interactive ruby interpreter.
The following things need to be true:
If these things are not all true for you, please see me.
IRB is the interactive ruby interpreter. If you have access to a command line and a copy of Ruby, you will have IRB. It's a testbed. It lets you try ideas out quickly. Here we're going to use it to write our first Ruby program.
At a command line, type
irb
to enter interactive Ruby. Got it up? Good. Now type:
puts "Hello Ruby you charming little thing"
Now press enter. See what you did there? Puts stands for put string. It just appends a string to the current output stream, which here is the terminal. Notice as well what you didn't do. You typed no semi-colons and no braces.
Now lets go a step further
In IRB, type:
puts "Hello Ruby " * 5
See what happened? Multiplication works on strings. This might seem odd at first, but it's actually consistent with the way that Ruby treats operators as methods. We'll come to this later.
IRB is tremendously useful for trying things out. Because Ruby is so expressive, you can do a huge amount just on one line. Ruby loves it when you write clean, concise, and above all, expressive code, and IRB can help you do this. You can tackle many of the exercises in this book using IRB.
Throughout this book you'll find lots fun and exciting exercises. I'd encourage you to try them out, but be pragmatic. If something is really simple and obvious, you can skip it, I won't mind. Here's a simple one for starters:
Write a line of code in IRB which prints our "Hello Ruby". Use puts to output a string.
You can create a simple loop using the times method:
10.times { puts 'cheese and crackers' }
Make irb print Hello World 50 times, 1000 times, 100000 times.
In this exercise we're going to create a simple ruby app and run it from the command line. The goal is just to make sure that our environment works and we can use it.
In this exercise we'll create a simple program that reads from the command line.
An if statement in Ruby looks like this:
if a
puts('a was true')
else
puts('a was false')
end
Let's talk about Variables. I'll assume here that you know what a variable is. Compared to some languages, Ruby is very free and easy about variables, it cuts you a lot of slack and assumes for the most part that you know what you are doing.
Variables in Ruby are duck typed. If the contents of a variable can quack like a duck, it will be allowed to swim on the pond. The interpreter will not do any type checking for you in advance. This might upset people coming from a .Net, C++ or Java background, and it's one of the reasons Ruby is suited to small teams of crack developers, since you can't enforce an interface, and can't easily prevent people from passing silly parameters to your methods.
You just have to trust to your basic intelligence. Scary?
It's also one of the reasons that Ruby is so fabulously productive, since you don't need to interact with the type system. Polymorphism is assumed. You don't need to do any work to enable it. Productivity wise, this is an enormous win, provided you can trust yourself and your co-workers.
Variables come into existence when they are first declared. There is no need to define them.
For example:
hi = "Hello Ruby"
some_big_number = 1000000
This is pretty sensible. A variable can hold anything you like, and the same variable can be re-purposed to hold something else entirely:
a = 10
a = "red"
There are naming conventions governing variable names in Ruby. These conventions are not enforced, but you should stick by them if you want people to like you, since Ruby is case sensitive.
Variable names always start with a lower case letter. By convention they are all lower case with optional underscores (snake case) eg:
number_of_people
user_name
height_of_the_eiffel_tower
Constants start with an upper case letter and by convention are CAPITALISED_SNAKE_CASE:
MAX_NUMBER_OF_PEOPLE = 20
NUMBER_OF_DAYS_IN_A_LEAP_YEAR = 364
Note that constants are not actually constant, you can redefine them if you really, really need to. You'll get a warning, but it won't break. Ruby is like this, it assumes you're clever. Yes, Ruby is a language that gives you the respect you DESERVE.
There are lots of little tricks you can do with variables that are useful, and can help a lot when trying to appear clever.
Assignments can be chained saving typing eg:
x = y = z = 4
puts x + y + z
=> 12
This works because the output of the assignment is the value that is being defined, so the output of z = 4 is 4.
Ruby also supports parallel assignment allowing you to assign multiple different variables on one line, eg:
a,b = 5,6
# a => 5
# b => 6
You can exploit this to swap the values of two variables in one line:
a,b = b,a
# a => 6
# b => 5
Everything in Ruby is an object including integers, or Fixnums as they are known in Ruby. Integers therefore have methods.
x = 1
x.to_s
=> '1'
You can do all the maths you would like with integers. It all works as you would expect:
1 + 2
=> 3
5 - 1
=> 4
3 * 4
=> 12
4 / 2
=> 2
You also have the common maths shortcuts you find in other languages. Again, these work as you would expect:
x = 2
=> 2
x += 5
=> 7
x *= 2
=> 14
Use ** for exponentiation should you feel that way inclined:
2 ** 2
=> 4
2 ** 3
=> 8
2 ** 4
=> 16
Remember how everything in Ruby is an object? This lets us do some reasonably clever things. For example, we can it to perform looping, like so:
5.times {puts "hello"}
hello
hello
hello
hello
hello
This might look strange. The little bit of code between the curly brackets {puts "hello"} is called a block. A block is an inline function. We are actually passing this block (or function) to the Fixnum times method, which is then executing it 5 times. This may seem a little odd at first, but it's actually really rather good and will soon become second nature, as we shall see when we reach the section on blocks.
As you would expect, greater than or less than signs to do comparison. == tests for equality.
1 > 2
=> false
2 >= 2
=> true
1 < 2
=> true
1 == 1
=> true
A tremendously useful one this, the spaceship operator returns 1, 0 or-1 depending on whether the first operand is larger, the same as or smaller than the second. This is excellent, as this type of comparison forms the basis of all known sorting algorithms.
To make any class sortable, all you need to do is to implement the spaceship operator on it. Telling a custom class how to respond to an operator is called operator overloading. More on operator overloading later.
4 <=> 3
=> 1
4 <=> 4
=> 0
2 <=> 10
=> -1
Underscores can be included in integers to make them more legible. We don’t do this often, but it’s a fun trick to know:
x = 100_000_000
y= 100000000
puts x == y
=> true
Ruby also has floating point numbers. Floats are useful for dealing with very large numbers, or high precision numbers.
Declare a float just by including a decimal point, like so:
a = 0.5
=> 0.5
a = 1.0
=> 1.0
You can convert integers to floats using the to_f method like so:
15.to_f
=> 15.0
Integers are not implicitly converted to floats, so:
3 / 2
=> 1
rather than 1.5
However, if one of the operands is already a float, the output will be a float so:
3.0 / 2
=> 1.5
3 / 2.0
=> 1.5
Floats have the rather handy ability of extending to infinity, like so:
1.0 / 0
=> Infinity
(1.0 / 0).infinite?
=> 1
(-1.0 / 0).infinite?
=> -1
If you need to say that something, say a stack, can contain an unlimited number of values, you might find a use for infinity.
Write a one line program that writes "Hello World" 250 times to the screen
Use times to write a program which outputs powers of 2, up to some maximum value, like this:
1
2
4
8
16
32
...
Ruby has an unusually rich and detailed String manipulation toolkit. String creation is similar to other languages
string_1 = "Hello Ruby"
string_2 = 'Hello Everyone'
puts string_1
=> Hello Ruby
puts string_2
=> Hello Everyone
You are free to use single or double quotes around your string.
Strings are real objects and have methods:
s = "Hello Ruby"
puts s.reverse
=> "ybuR olleH"
As you might expect, you can create strings, and call methods on them directly.
"Hello Ruby".upcase
=> "HELLO RUBY"
Use double quotes and the #{} syntax if you want Ruby to look for variables embedded in a string:
name = "Derrick"
puts "Hello, my name is #{name}"
=> "Hello, my name us Derrick"
Nice, simple, inline, readable.
You can include escape characters in your string with a backslash:
"Ruby's great! \n Oh yes it is!"
=> "Ruby's great"
=> "Oh yes it is!"
Add two strings together using simple arithmetic:
"Hello " + "Ruby"
=> "Hello Ruby"
"Hello " << "Everybody"
=> "Hello Everybody"
"Hello " * 5
=> "Hello Hello Hello Hello Hello"
More likely you will want to do a join on an array, something more like this:
["Hello", "Ruby"].join(' ')
Strings can be converted to lots of other types using the casting methods. There are lots of these built in, but feel free to write your own as well. The "to_i" method converts to an integer.
"5".to_i
=> 5
"99 Red Balloons".to_i
=> 99
Strings can be created from any other type using the to_s method. For example, if you have an integer, and you need it to be a string, do it like this:
5.to_s
=> "5"
Review the above and attempt the following
The gsub String method gives us is global substitution. Read about it here:
http://ruby-doc.org/core-2.1.4/String.html#method-i-gsub
I have an issue where I commonly type a instead of e. It's a terrible problem for me, but you can help. Write code to replace all instances of the letter 'a' in a string with the letter 'e'.
Check out the string API here:
http://ruby-doc.org/core-2.2.2/String.html
In my application I have a string like this:
email = " dave@davely.com "
I need rid of that whitespace. Please clean it up for me.
Now I have a string like this:
first_name = " dave"
I need it to be "Dave"
Please tidy it so we can save it to the database.
The Time class will allow you to easily format a Time object as a String using strftime.
Get the current time using Time.now
Now review the strftime api here http://ruby-doc.org/core-2.2.2/Time.html#method-i-strftime
Now output the time in this format:
"20 Jan 1946 at 12:45"
Functions are declared using the def keyword:
def greeting
puts "Hello Ruby"
end
greeting()
=> Hello Ruby
Functions can accept parameters as you would expect. We pass them like this:
def greet(name)
puts "hello #{name}"
end
greet("dave")
=> "hello dave"
When calling a function, the braces are optional.
greet "dave"
=> "hello dave"
This is a really nice syntax, and comes into it's own when we start writing methods.
Functions can return a value. We pass back a value using the return statement, like so:
def say_hello_to(name)
return "hello #{name}"
end
puts say_hello_to "dave"
=> "hello dave"
get_greeting_for "dave" evaluates to the string "hello dave". This string is received by puts, which then outputs it to the screen.
The return statement is also optional. If it's omitted the function will return the last evaluated expression, so:
def get_greeting_for(name)
"hello #{name}"
end
puts get_greeting_for "dave"
=> "hello dave"
This is a clean and useful syntax for short methods such as getters.
We can set the default value of an argument, so if no value is passed, our function will still work:
def get_greeting_for(name="anonymous")
return "hello #{name}"
end
puts get_greeting_for
=> "hello anonymous"
Note that if we have several arguments, and some are missing, they will be filled in from left to right, so the last ones will take their default values.
Write a simple function that greets a person by name. It should receive a name and return a string.
If it is called without parameters it should say "Hello anonymous"
Write a function which receives a value and outputs a string containing all the numbers up to and including that value.
Integrate this into a command line app.
Ruby loves it when you tell it what to do.
If statements are present. They work as you'd expect. Notice that no braces are required.
bacon = true
fish = false
if fish
puts 'I like fish'
elsif bacon
puts 'I like bacon'
else
puts "I don't like fish or bacon"
end
=> 'I like bacon'
The unless keyword is the opposite of the if keyword. It's equivalent to !if (not if). It can make your code more readable.
bacon = false
unless bacon
puts 'fish'
else puts 'bacon'
end
=> "fish"
Ruby loves it when you write things concisely. The if and unless keywords can also be placed after the line of code you may or may not want to execute. When used in this way they are called statement modifiers:
user = "dave"
puts "Hello #{user}" if user
puts 'please log in' unless user
Like most languages Ruby includes a ternary operator. It works as you'd expect. If the first portion evaluates to true the first result is given, otherwise the second result is given:
bacon = true
puts bacon ? 'bacon' : 'fish'
=> "bacon"
This is equivalent to:
bacon = true
if bacon
puts 'bacon'
else
puts 'fish'
end
=> "bacon"
Booleans in Ruby are logical and clear
Only Nil and False are false in Ruby. If it exists it's true. That includes zeros, empty strings, etc. This is because 0 is an object in Ruby, as is the empty string ""
We can test this with a short function, that determines if the parameter evaluates to true or false, like so:
def true?(value)
if (value)
true
else
false
end
end
true?(false) # => false
true?(nil) # => false
true?(0) # => true
true?("") # => true
true?(true) # => true
true?(15) # => true
true?([0,1,2]) # => true
true?('a'..'z') # => true
true?("pears") # => true
true?(:bananas) # => true
A variable is nil if the variable has been declared but doesn't point to anything (remember Ruby is fully object oriented so all variables are pointers to objects)
A variable is nil if it has been declared, but holds no value. Nil exists as a type, and has methods. For example:
a = nil
a.to_s
=> ""
a.nil?
=> true
Nil is a very useful thing to be able to return. It means "no value".
A variable is undefined if it has not been declared. You can test for this using the defined? operator.
a = 1
defined? a
=> "local-variable"
defined? b
=> nil
You can do all the standard things using Boolean algebra.
true && true
=> true
true || false
=> true
12 == 12
=> true
are all supported.
There are useful things that can be done with the OR || command. The second part is only evaluated if the first part returns false (nil or false evaluate to false), and the return value is the last value calculated. Rails exploits this letting you do neat things like this:
name = nil
user_name = name || "Anonymous Coward"
Here we have a default value. If name is nil, anonymous coward will be used instead.
A simple one to start with. I have a boolean variable called hungry:
hungry = true
I want to choose a name for my cat, but for personal and ideological reasons I am only interested in cat names which start with the letter R.
You can get the first letter of a string using the square bracket syntax:
"hello"[0]
# => "h"
Employ a while loop to loop over the function until an acceptable name is suggested. A while loop in ruby looks like this:
x = 0
while x < 5
x += 1
puts x
end
Catch the case where I accidentally type "Ruby the Cat" instead of "ruby the Cat". The easiest way to do this is with a downcase.
http://ruby-doc.org/core-2.1.0/String.html#method-i-downcase
Of course Ruby also has case statements. You almost never see these in the wild as case statements are a bit, well, 1990s, but if you should decide you need one, here's how they work.
refreshment_type_required = "biscuit"
suggest_you_eat = case refreshment_type_required
when "pastry" : "cinnamon danish whirl"
when "hot drink" : "mocha with sprinkles"
when "biscuit" : "packet of bourbon creams"
else "glass of water"
end
A case statement will break automatically if you hit a matching term, you don't need to tell it to break as with some other languages.
A Block is an unnamed function, a lambda, which is received by a function, and which can tell it what to do.
Blocks are where the fun really starts. A block in Ruby is a sort of unnamed function that can be passed to a method using a special syntax. It can be as long or as complicated as we like, and can span many lines. Here's a simple example:
5.times {puts "Ruby Ruby"}
Ruby Ruby
Ruby Ruby
Ruby Ruby
Ruby Ruby
Ruby Ruby
The block here is the bit of code: puts "Ruby Ruby". This piece of code is passed to the times method. We use blocks for everything in Ruby, most notably when looping, but in plenty of other ways too. Come with me now as we enter the world of loops…
This is super great because it means that the number 5 knows in itself how to iterate up to itself. We have perfect encapsulation. We don't need a for loop. We don't need to make any assumptions about implementation. It's all hidden under the surface.
We can have our function pass parameters to it's block. We do this using the | | "chute" syntax. For example:
5.times{|i| puts i}
0
1
2
3
4
Here the times method passes a number to the block, which is available in the variable i. This is one of the many ways in which we do iteration in Ruby.
One of the most common and programmer friendly applications of blocks is in looping. Many objects, most notably, Arrays and Hashes will accept a block then apply that block to each of their members in turn. All the looping code is encapsulated, meaning we don't need to worry about the internal structure of the array.
people = ["jim","harry","terrence","martha"]
people.each { |person| puts person }
=> "jim"
"harry"
"terrence"
"martha"
As we saw earlier, the Fixnum.times method works in a similar way:
5.times { puts "Ruby" }
=> Ruby
Ruby
Ruby
Ruby
Ruby
If we wanted to iterate over some specific numbers, we might use the upto method to create an Enumerable object, and then iterate over that. Observe:
5.upto(10) {|i| puts i}
=> 5
6
7
8
9
10
We could also iterate over a Range object. more on Ranges shortly:
(6..8).each {|i| puts i}
=> 6
7
8
The most common use of a loop, iterating over an array is totally covered, and in fact, when writing Ruby code, we almost never write the sort of looping constructs you might be used to. Blocks have them covered
If for some reason we need t get the index of an array, we can do this too using Array.each_with_index.
This method accepts a block with two parameters, the value and an index. We can use it like so:
people = ["jim","harry","terrence","martha"]
people.each_with_index { |person, i| puts "person: #{i} is called #{person}" }
=> person: 0 is called jim
person: 1 is called harry
person: 2 is called terrence
person: 3 is called martha
We can declare a block in two ways. We can use the curly braces syntax, or the do/end syntax.
The difference between them is that the curly braces syntax can only take a single line of code, whereas the do/end syntax can take as much code as you like, even an entire web page template if needed.
Here is an example of the curly braces syntax:
favourable_pets = ["kittens","puppies","hamsters"]
favourable_pets.each_with_index { |pet, i| puts pet; puts i }
=> kittens
puppies
hamsters
And here is an example of the do/end syntax. Notice the code is more spread out. This is more readable for large blocks of code.
favourable_pets.each_with_index do |pet, i|
puts pet
puts i
end
=> kittens
0
puppies
1
hamsters
2
The String.gsub method will find and replace substrings in text. It's terribly useful, but sometimes we need more, we need to find and manipulate strings in text.
Say you have a string containing URLs, maybe culled from twitter. You could replace all the urls like this:
tweet_text = "hello http://www.google.com hi"
tweet_text.gsub(/http:\/\/[^ ]*/, "A URL was here.")
=> "hello A URL was here. hi"
This is OK, but what if we wanted to replace the URL with a functioning link. The gsub method will optionally accept a block. The block receives a parameter which contains the match. The block must then return a value which is used to replace the match.
We can split a string about an array using the split function, like this:
'hello world'.split(' ') => ['hello', 'world']
Use string.split and Array#map method to take a sentence and reverse the letters of all the words, like so.
"Hello there" # => "olleH ereht"
First split, then map to reverse.
For Bonus points, capitalise the first letter to produce:
"Olleh ereht"
Inject is a ridiculously handy function that will allow you to inject a block between each element of an array. The block will in turn receive the next element in the array and the output of the previous call to the block.
I have an array of numbers like this:
[4,9,6,3]
Filing also uses a block. We call File.open and pass it a block. Within the block we have access to the file object.
File.open("path/to/file", 'wb') do |file|
file.write('hey there!', :ruby)
end
Write code which saves a random string to a file.
There are many test harnesses for Ruby. RSpec is the most popular. It has inspired many imitators such as Jasmine for JavaScript and PHPSpec for PHP.
Install rspec using
gem install rspec
or if you prefer add it to your Gemfile and bundle.
You now have a new terminal command, rspec. Type:
rspec
in a terminal to test your installation.
RSpec is very easy to setup. Create a folder called spec in the same directory as your code. This is where your specs will live.
In the spec folder create a file called test_spec.rb. Place the following code in it:
describe "an example spec" do
it "passes" do
expect(true).to be(true)
end
end
run your specs with:
rspec spec/*
You should see a message like this, telling you the specs have passed:
Finished in 0.00105 seconds (files took 0.11919 seconds to load)
1 example, 0 failures
Let's write an alarm clock function to wake us up in the morning. When we call it it's going to give us a message: "Beep beep beep". This will help us wake up.
def wake_me_up
"Beep beep beep"
end
Save this in a file called clock.rb
Now let's write a spec for it. First we need to import our clock, then we call the code, then we say what the result should be:
require_relative '../clock'
describe "alarm clock" do
it "beeps" do
expect(wake_me_up).to eq('Beep beep beep')
end
end
Run this and verify it works
This is all well and good, but what about Test Driven Development (TDD). In this methodology we write the test first, watch it fail, then write code to make it pass.
Some-days I feel sleepy and I'd like to be woken with more force. I'd like my alarm clock to accept an optional parameter and wake me up more strongly.
First we write the spec
require_relative '../clock'
describe "alarm clock" do
it "beeps" do
expect(wake_me_up).to eq('Beep beep beep')
end
it "beeps louder" do
expect(wake_me_up(6)).to eq('Beep beep beep beep beep beep')
end
end
Now we run the spec and we see red:
1) alarm clock beeps
Failure/Error: expect(wake_me_up(6)).to eq('Beep beep beep beep beep beep')
ArgumentError:
wrong number of arguments (1 for 0)
# ./clock.rb:5:in `wake_me_up'
# ./spec/clock_spec.rb:16:in `block (2 levels) in <top (required)>'
Finished in 0.00161 seconds (files took 0.14819 seconds to load)
2 examples, 1 failure
Failed examples:
rspec ./spec/clock_spec.rb:15 # alarm clock beeps
Notice it's telling us exactly what and where the problem is. Let's write code to make our test pass.
def wake_me_up(i = 3)
("beep " * i).strip.capitalize
end
Run the test again and success, we have a passing test.
Many developers say they live for these moments of green. You may or may not love testing as much as this but regardless, a good suite of passing tests can give you piece of mind at the weekend and help you sleep better at night, knowing you have done a good job.
Review the RSpec matcher documentation here:
https://www.relishapp.com/rspec/rspec-expectations/docs/built-in-matchers
In the section on blocks you wrote code to reverse the individual words in a string, but not the string itself, such that:
"Hello World"
becomes:
"olleH dlroW"
Like most languages Ruby allows you to create Arrays to hold multiple values. Arrays in Ruby are one dimensional, though you can declare an array of arrays if you feel the need.
Create an array using square brackets like so:
cakes = ["florentine", "lemon drizzle", "jaffa"]
Access an array using square brackets:
cakes[0]
=> "florentine"
Arrays are zero indexed, so 0 is the first element.
As you might expect from Ruby, arrays can hold any type of element. This is allowed:
["hello", 1, 0.5, false]
Arrays can be manipulated in a huge variety of ways For example:
[1,2,3] + [4,5,6]
=> [1, 2, 3, 4, 5, 6]
[1,2,3] - [1,3]
=> [2]
[1,2,3] << 4
=> [1, 2, 3, 4]
[1,2,3] * 2
=> [1, 2, 3, 1, 2, 3]
There are also a raft of array methods we can use, such as:
[1,2,3].reverse
=> [3, 2, 1]
[1,2,3].include? 2
=> true
You might notice here that this method name has a question mark in it? This is a Ruby convention. Methods with a question mark return true or false.
[4,9,1].sort
=> [1, 4, 9]
Parallel assignment works when pulling values out of a array.
array_of_numbers = [1,2,3,4]
a,b = array_of_numbers
a
=> 1
b
=> 2
We can also pull the first element, and return the rest of the array should we wish to:
arr = [1,2,3]
a,*arr = arr
a
=> 1
arr
=> [2, 3]
This is a clever trick to know as it impresses people and make you look brainy.
The to_a method allows many objects to be converted to arrays. For example an array can be created from a range as follows
(1..10).to_a
=> [1,2,3,4,5,6,7,8,9,10]
Ranges are useful objects that can be used to represent a sequence. Ranges are defined using the .. or … syntax. For example:
1..10
represents the sequence of numbers between 1 and 10.
1...10
represents a range that excludes the high value. In this case the numbers 1 to 9
Ranges are compact. Every value in the range is not held in memory, so for example the range:
1..100000000
…takes up the same amount of memory as the range:
1..2
Ranges can be converted to arrays using to_a like so
(1..10).to_a
=> [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
Note we need to put braces around the range here to disambiguate the dots.
We can also declare arrays of characters like this:
'a'..'f'
We can use the equality === operator to test if a range contains a value. For example:
('a'..'f') === 'e'
=> true
('a'..'f') === 'z'
=> false
We can pass a block to an array to be executed on each item in that array. For example:
['fork','knife','spoon'].each {|table_item| puts table_item}
Our code doesn't need to know the internal details of the array, it just passes a block, and lets the array sort itself out.
['cats','hats','mats','caveats']
1. cats
2. hats
3. mats
4. caveats
Repeat the exercise above, but now only print out every string with an odd number index.
Investigate the remove method. Modify your code to only print out only strings which contain the letter 'c'
Functions which return nothing are hard to test. Modify your code so that instead of putting to the screen it returns a string. Use RSpec to test it.
Use the join method to join the array together with commas, you should get something like this:
Fluffy, Hammy, Petunia
Reverse the order of the array, output something like this:
Petunia, Hammy, Fluffy
Create an array of dogs. append it to the first. using the plus operator, then output both arrays together, like this:
Fluffy
Hammy
Petunia
Use Map to capitalise each element in the array prior to output, like so:
FLUFFY
HAMMY
PETUNIA
Now capitalise and reverse each element in the array.
PETUNIA
HAMMY
FLUFFY
Use sort to sort the array alphabetically like so:
BARKY
FLUFFY
GOMEZ
HAMMY
PETUNIA
WOOFY
And sort the array in reverse order:
WOOFY
PETUNIA
HAMMY
GOMEZ
FLUFFY
BARKY
By default array.sort will sort any array of values using the <=> spaceship method of the value. This method as we have seen returns -1, 0 or 1 depending on whether the value is lower, equivalent or greater than another value. The String class implements the <=> method by comparing the two values alphabetically, so by default array.sort sorts an array of strings alphabetically like so:
array = ["James", "Derek", "Stuart", "Thomas"]
puts array.sort
=> Derek
James
Stuart
Thomas
If we want to override this, there are two easy ways to do it. First we can override the spaceship operator (coming soon). Alternately, we can pass Array.sort a block which can be used instead. For example the following code sorts an array in order of the second letter:
array = ["James", "Derek", "Stuart", "Thomas"]
puts array.sort {|a,b| a[1] <=> b[1]}
=> James
Derek
Thomas
Stuart
Nice and Simple.
You can get the length of the string using "string".length.
BARKY
WOOFY
HAMMY
GOMEZ
FLUFFY
PETUNIA
Map is an insanely useful array function that lets you modify each element of an array using a block.
Say you have an array of strings:
['kittens', 'puppies', 'hamsters']
Lets say you want to convert this to an array of symbols, you could do something like
['kittens', 'puppies', 'hamsters'].map {|i| i.to_sym}
Use the Array#map method to generate a random string. You can generate a random letter by making an array of acceptable characters like so:
n = (0..9).to_a + ('a'..'z').to_a + ('A'..'Z').to_a + %w(_ -)
You can then pull a random one out like so:
n.sample
Now use map to generate a 128 character random string.
Hashes are a big deal in Ruby. We use them a lot, particularly when passing bits of data around. Symbols are tiny lightweight Ruby placeholder objects. They are often used in conjunction with hashes. In this section we will look at hashes, symbols, and some of their common uses.
Symbols often seem like magic runes to Ruby newcomers. In fact they’re just little objects that have some special features and syntax surrounding them to make them a little easier to use and a little lighter on the computers memory.
Symbols are defined using the colon operator or the to_sym method:
:price
:length
:outrageousness
"My Price".to_sym
A symbol will only exist once in memory, no matter how many times it is used. If for example, you create two symbols in different places both called :name for example, only one object would be created. This object would persist for as long as the Ruby interpreter was running.
Symbols are most commonly used as placeholders in Hashes. We have used symbols already in Rails, for example the Rails params hash associates any the values of any parameters passed in by the user (from a form or in the url) with a symbol representing the name of that value.
Hashes are objects that associate lists of arbitrary objects with each other. For example:
animals = Hash.new
animals[:tall] = "giraffe"
animals[:minute] = "kitten"
puts animals.inspect
animals = {:tall => "giraffe", :minute => "kitten"}
puts animals[: minute]
=> kitten
A hash will return nil if it can't find any matching keys. You can set a default value that a hash will return in this instance should you so desire.
animals = Hash.new("monkey")
puts animals[:funny]
=> "monkey"
You can also set this value using the Hash.default method
animals.default = "star mole"
puts animals[:odd]=> star mole
Any Object can be used as a key. Here we use the number 45 as a key, and store the root directory of the local file system as a value:
animals[45] = Dir.new '/'
puts animals.inspect # {45 => #<Dir:0x284a394>
Create a hash to hold a list of feelings and foods. It should look like something like this, only longer:
food_hash = {
:happy => "ice cream",
:pensive => "witchetty grub"
}
Write a function that allows a user to type in a feeling and have it return the corresponding food (Tip: use to_sym to convert the user's input into a symbol)
A function can receive a hash of values. This is tremendously useful, and we do this all the time.
def get_greeting_for(args={})
name = args[:name] || "anonymous"
return "hello #{name}"
end
puts get_greeting_for :name => "Fat Tony"
=> "hello Fat Tony"
puts get_greeting_for
=> "hello anonymous"
Here our function receives a parameter we've called args. The default value of the args parameter is an empty hash. Any key value pairs we pass will go into args, and can be pulled out.
On the second line we do this:
name = args[:name] || "anonymous"
Here we set the value of name to be either the value stored in args under the :name key, or if this evaluates to nil (and therefore false) we set it to anonymous. This is tremendously useful, since we can create functions that accept multiple arguments, in any order, with any defaults that make sense.
You should get used to writing configurable, extensible methods that receive a hash. This is a real rubyism.
Extend your food finder function so it can receive a hash. You should be able to call it like this:
food mood: :pensive
Write RSpec to verify it does in fact work.
A substitution cypher is one in which each letter is changed for another, so 'a' might map to 'z' and 'b' might map to 'y'.
Here we will create a substitution cypher in a very few words.
We can use zip to combine two arrays
[1,2,3].zip [4,5,6]
=> [[1,4],[2,5],[3,6]]
we can compose an array of characters from a range like this:
('a'..'z').to_a
We can create a Hash from arrays of arrays, like so:
Hash[[[1,4],[2,5],[3,6]]]
=> {1 => 4, 2 => 5, 3 => 6}
You can rotate an array using rotate, like this
[1,2,3].rotate 1
=> [2,3,1]
Use these techniques to create a substitution cypher hash, something like this:
{'a' => 'b', 'b' => 'c', c => 'd' ...}
If you have get everything right you should be able to pass the string back though, and it will go back to how it was.
For bonus points, write the function body in two lines of code.
Modify your substitution cypher so it can receive a hash of values.
I want to be able to call it like this
substitution_cypher "Hello Ruby", rotate: 3
Ruby is Object Oriented. Many languages claim to be object oriented, but most fall short in some area or another. When Ruby says it's object oriented, it really, really means it.
We have used lots of Ruby objects so far, when we have done things like:
"Abracadabra".reverse.downcase
and
Time.now
Lets look now at how we can define our own objects.
Ruby has a class based object model. This means we define classes, then use them to stamp out as many objects as we like. Of course, this being Ruby, classes are objects and have methods in their own right, but we'll get to this soon enough.
We define a class like so:
class Pet
end
Here we have defined a very simple class, called Pet. By convention, classes in Ruby always start with a capital letter. Now lets create a pet:
flopsy = Pet.new
This is great, we have created a new instance of the Pet class. Flopsy is an instance of Pet.
Note that all objects get a new method for free. This they inherit from the Object superclass. More on inheritance in a bit.
We can go in reverse, to find the class of an instance like this
flopsy.class
=> Pet
Ruby doesn't shy away from introspection, it comes baked in, as we shall see later.
This is all very nice, but flopsy is not very interesting, she can't walk the tightrope or play chess, or really do anything much. To make Flopsy more interesting, we need a method:
class Pet
def play_chess
puts "Now playing chess"
end
end
Here see now. We have added a play chess method to flopsy. We can now write:
flopsy.play_chess
…and she will, after a fashion. She is only a housepet after all.
There are a few things to bear in mind when naming methods in Ruby if you want to appear cool and down with the kids.
First, use snake case for all function names, like this.
each_with_index
Second, if your method returns a boolean, and is a question, frame it as such. Use a question mark, like so:
['toast','jam','honey'].include? 'ham'
person.has_name?
password.valid?
Third, if your method modifies the original object in place, rather than returning a new object, and is therefore destructive, indicate this with an exclamation mark, like so:
['toast',['jam','honey']].flatten!
=> ['toast','jam','honey']
Flopsy is still a little dull. It would be great to be able to store some data about her, maybe give her some custom attributes.
In Ruby we save an instance variable using the @ syntax. Instance variables are @ variables. All instance variables are private, so to get at them, we need to write methods called getters and setters to access them. Lets have a look now:
class Pet
def super_powers=(powers)
@super_powers = powers
end
def super_powers
@super_powers
end
end
Here we have given flopsy two methods, a getter and a setter. The first is a setter. The super_powers= method receives a parameter and stores it in an instance variable called @super_powers.
The second is a getter. It simply returns the @super_powers instance variable that was previously set.
We can now set flopsy's super power like this:
flopsy.super_powers = "Flight"
and retrieve it like this:
flopsy.super_powers
=> "Flight"
Note we don't have to declare the @super_powers variable anywhere. We can just set it, and that's fine.
Getters and setters give us a clean way to provide an interface onto our object. It insulates us from implementation details. We are free to store the data in any way we wish, as a variable, as a file, in a database, in an encrypted hash, or as a combination of other variables.
This is how active record works when using Rails. Values can be got from the database as though we were accessing object attributes.
Just like Flopsy, the boundary between attributes and methods is far more fuzzy than in most languages. This is partly because of Ruby's optional parentheses, which make it look as though we are accessing attributes, when in fact we are always accessing methods.
We can have read only attributes by only creating a getter, and write only attributes by only creating a setter. An example of a write only attribute would be a password, which might get set, and then encrypted with a one way hash, never to be read again.
Since class variables are so common, ruby defines shortcuts for creating them and their associated getters and setters. The attr method creates an attribute and its associated getter. If the second parameter is true a setter is created too. The attr_reader and attr_writer methods create getters and setters independently.
The initialize method is called by the new method and it is here that we put any code we need to initialize the object.
When flopsy's sidekick mopsy was first created, she didn't have any powers at all, observe:
mopsy = Pet.new
mopsy.super_powers
=> nil
Poor mopsy. We can remedy this situation by giving mopsy a basic superpower when she is initialised. Lets do this now.
class Pet
def initialize(args = {})
@super_powers = args[:power] || "Ability to eat toast really really quickly"
end
end
Now when we recreate mopsy, she comes already tooled up
mopsy = Pet.new
mopsy.super_powers
=> "Ability to eat toast really really quickly"
We can also do this:
mopsy = Pet.new :power => "none worth mentioning"
mopsy.super_powers
=> "none worth mentioning"
If you need to see a list of all mopsy's attributes you can do so using inspect like so:
mopsy.inspect
=> "#<Pet:0x102f87fe8 @super_powers="Ability to eat toast really really quickly">"
Everything is an object, so it’s important that we get a whole lot of practice in with making them. In this series of exercises we will create a class, implement some methods, overload some operators and create some virtual attributes.
Extend the warship / flufster class you created earlier so it can receive a hash of initial values.
Write an initializer method that accepts a hash and sets default values appropriately. You should be able to call it like this:
Fluffster.new age: 2, name: "Floppy"
Virtual attributes are particularly useful for things like passwords where you don't actually want to store the password anywhere, or allow retrieval.
Because Ruby is an interpreted language objects are open and can be modified at runtime. Classes can be reopened at any time.
We can give mopsy new methods, even after she has already been created. Observe:
class Pet
def play_chess
puts "now playing chess"
end
end
class Pet
def shoot_fire
puts "activating primary weapon"
end
end
mospy.shoot_fire
=> activating primary weapon
Mopsy can still play chess. The Pet class was added to, not overwritten
mopsy.play_chess
=> Now playing chess
As we've mentioned before existing classes can be extended. This includes built in Ruby classes. This is a feature that can be used both for good, and for evil:
For good
class String
def put_times(n)
for i in (1..n)
puts self
end
end
end
"Marmalade Toast".put_times 5
For Evil
class Fixnum
def *(num)
self + num
end
end
puts 5*4
=> 9
Yes, Ruby lets you do this. Be careful and do things and your code will read like liquid sunlight.
Reopening code in this way is often known as monkey patching. We can modify or extend any existing class at runtime, even built in classes like strings and arrays. This can be used to great effect, for example Rails Fixnum date extensions, which allow you to type things like:
Date.today + 5.days
Here Fixnum has been monkey patched with an function that allows it to work with dates and times. This is a nice syntax, although it makes some people cross as it appears to break encapsulation.
Monkey patching is fun, but use it with care, otherwise you'll end up with a twisted mess on the floor.
Monkey patching is the first step towards meta-programming - writing code which writes code. More on this soon.
Extend Fixnum with a method .seconds which returns the number * 1000
You can now call Time.now + 60.seconds to get the time in one minute.
For bonus points, also create minutes, hours, days and weeks methods. You can now call Time.now + 1.week.
Extend the FixNum class with a green_bottles method that returns the lyrics for the popular song.
I want to be able to say:
5.green_bottles
and get back:
"5 green bottles sitting on the wall
4 green bottles ..."
If you are running a Macintosh, turn the sound up and try this. Note the backticks which you can find above the alt key:
`say #{5.green_bottles}`
For bonus points, make it accept a block that receives the song line by line. I want to be able to call:
5.green_bottles {|song_line| puts song_line}
Extend the Array class with a method that iterates over every other element. Call it like this:
names.every_other {|name| puts name}
You will need to explicitly receive the block, filter the array, then pass it to the each method.
Did I mention that in Ruby everything is an object? This extends to operators, such as +, -, * and /. Operators in Ruby are actually methods, and we can define and redefine them, like so:
class Pet
def +(pet)
p = Pet.new
p.super_powers = self.super_powers + " and also " + pet.super_powers
return p
end
end
Here we have defined a plus method that receives another pet. This simply creates a new pet with the combined superpowers of it's two parents and returns it. Observe the offspring of Mopsy and Flopsy:
cottontail = mopsy + flopsy
cottontail.super_powers
=> "Ability to hop really really quickly and also Flight"
In this section you will extend your warship / flufster class with some operator overloading.
Implement the + operator. Have it return a new object with the names and standard actions concatenated.
A slightly more useful one this time.
You can now call the .sort method on an array of objects, and it will be sorted by attack power.
Implement the * operator.
Have it return an array containing multiple instances of the object. (either copies, or several variables pointing to the same object, free choice.) You can duplicate an object using the .dup method.
Rails supports single object inheritance. This means a class can have one parent class and will inherit all the methods and attributes belonging to that class. We define inheritance relationships using the < operator.
For example, let's say we'd like to define a particular type of pet, say a small and fluffy kitten. Let's create a kitten class that can inherit from our Pet class:
class Kitten < Pet
def play_tennis
puts "I am now playing tennis"
end
end
Our kitten now has all the attributes of a pet. It can shoot fire from it's eyes, and play some good chess, but in addition it can also play tennis:
tiger = Kitten.new
tiger.play_tennis
=> I am now playing tennis
tiger.shoot_fire
=> now shooting fire
Extend your Fluffster / Warship exercise from before. Create a subclass of warship, perhaps a frigate, that has different abilities.
In Ruby, all methods exist within a class. When you create an object, the methods for that object exist within it's class. Methods can be public, private or protected, but there is no concept of a static method. Instead, we have singleton classes, commonly referred to as eigenclasses.
Static methods are class methods. they belong to the class, not the instance. This would break Ruby's simple object structure, since classes are instances of class Class, adding methods to Class, would make them available everywhere, which is not what we want.
Instead, Ruby lets us define an unnamed singleton class that sits in the inheritance tree directly above any object. Lets do this now and create a static method.
class Kitten
class << Kitten
def max_size
8
end
end
end
The class « Kitten syntax opens up the eigenclass and pops the max_size method within it. We can then access it like this
puts Kitten.max_size
Notice that we are talking to the Kitten class as an object here.
We use the class « self syntax to explicitly open an object's eigenclass. We can accomplish the same thing using the shorthand syntax:
def Kitten.max_size
8
end
This adds a method to the Kitten eigenclass. We can also add a method to the eigenclass of any other object, like so:
fluffy = Kitten.new
popsy = Kitten.new
def popsy.deploy_wheels
@wheels = :deployed
end
def popsy.launch_scouter
@scouter = :launched
end
Here we have added a method to popsy's eigenclass, allowing her to deploy wheels.
The eigenclass sits directly above the object in the inheritance hierarcy, below the class of the object. It provides a handy place to put methods hat we want to apply directly to the object, rather than to every instance of that object. It feels technical, but once you get it, it's actually rather nice.
In irb (or in a ruby file) create 3 instances of your warship/pet class. Add a different method to each of them. Verify that only the instance you added the method to it to can call it.
You're writing to an eigenclass. Feels natural doesn't it?
Modules can be used to add reusable functionality to a class. They are sometimes known as Mixins. A module consists of a whole bunch of methods. By importing it into a class, we gain access to all those methods. This is a handy way to get around the restrictions of single object inheritance, since we may import as many modules as we like.
Lets teach flopsy how to make an omelette. Then she will be able to help out in the kitchen. It would be nice if our omelette could accept a few options, so lets allow that too.
module CookOmelette
def cook_omelette(args={})
number_of_eggs = args[:number_of_eggs] || args[:eggs] || 2
cheese = args[:cheese] ? "cheese" : nil
ham = args[:ham] ? "ham" : nil
mushrooms = args[:mushrooms] ? "mushrooms" : nil
ingredients = [cheese,ham,mushrooms].delete_if{ |ingredient| ingredient.nil? }
ingredients = ingredients.join(" & ")
"#{ ingredients } omelette with #{number_of_eggs} eggs".strip
end
end
Now include the mixin in the Pet class.
class Pet
include CookOmelette
end
All our pets can now make delicious omelettes. Observe:
mopsy.cook_omelette
=> "omelette with 2 eggs"
mopsy.cook_omelette :ham => true, :cheese => true, :eggs => 4
=> "cheese & ham omelette with 4 eggs"
Including a mixin in a class adds those methods to that class as though they had been defined within that class.
Methods added to a class by a module are inherited by subclasses of that class. For example, by including the CookOmelette mixin in the Pet class the Kitten subclass and all its instances also gain that method.
This exercise extends the lethal warship of fluffy kitten class .
Lets Create a module now. Extract the age and age_in_weeks methods into a module that can be included elsewhere. Name the module sensibly. Now remove these from your class, and instead import the module. You now have the ability to make anything have an age, and to query it's age sensibly.
Write a stereo module that allows your class to play some cool random sounds (really strings). Add it to your class.
Include and Extend allow us to take methods from a module and add them to an object. They work slightly differently from each other though. Let's take a look...
Extend adds methods to an object. It extends that object by adding new features to it.
class Hamster
end
module PetSkills
def snuggle;end
end
Hamster.extend PetSkills
If you extend a class, you create a class method.
h = Hamster.new
Hamster.methods.include? :snuggle
# => true
h.methods.include? :snuggle
# => false
If you extend an instance of a class, you create an instance method, but only on that instance. You can extend any object like this.
h.extend PetSkills
h.methods.include? :snuggle
# => true
i = Hamster.new;
i.methods.include? :snuggle
# => false
You can call extend on any object to add methods to that object alone.
Include takes a more traditional approach. If you include a module in a class, the methods in the module will be added as instance methods, and will be available to all instances of that class.
class Gerbil
include PetSkills
end
g = Gerbil.new
Gerbil.methods.include? :snuggle
# => false
g.methods.include? :snuggle
# => true
Extend will add methods to an object, and only to that object. If we extend a class we get class methods.
Include will include methods from a module into a class, those methods become instance methods for objects of that type.
Exception handling in Ruby is very similar to other languages.
Raising an exception in Ruby is trivially easy. We use raise.
raise "A Error Occurred"
This will raise the default RuntimeException.
We can also raise a specific type of exception:
value = "Hi there"
raise TypeError, 'Expected a Fixnum' if value.class != Fixnum
We can rescue exceptions easily. Put the code that might raise an exception in a begin, rescue end block. If an exception occurs, control will be passed to the rescue section.
begin
raise "A problem occurred"
rescue => e
puts "Something bad happened"
puts e.message
end
We can rescue different types of exceptions
value = "Hi there"
begin
raise TypeError, 'Expected a Fixnum' if value.class != Fixnum
raise "A problem occurred"
rescue TypeError => e
puts "A Type Error Occurred"
puts e.message
rescue => e
puts "an unspecified error occurred"
end
Here are the built in exceptions available in Ruby:
Exception
NoMemoryError
ScriptError
LoadError
NotImplementedError
SyntaxError
SignalException
Interrupt
StandardError
ArgumentError
IOError
EOFError
IndexError
LocalJumpError
NameError
NoMethodError
RangeError
FloatDomainError
RegexpError
RuntimeError
SecurityError
SystemCallError
SystemStackError
ThreadError
TypeError
ZeroDivisionError
SystemExit
fatal
You can define your own exceptions like so:
class MyNewError < StandardError
end
You can then raise your new exception as you see fit.
Try these exercises to get a feel for exception handling in Ruby.
Extend your kitten class from yesterday. Lets assume your kitten needs an age (0 will not do) Raise an argument error if age is not set in the initialiser
Your kittens age must be a Fixnum. Check for this, if it is not, throw a Type Error
Can your kitten do maths? If not, write a divide function now that accepts two values and divides them. Catch the division by zero error, and if it occurs, return nil.
So how does a method yield control to a block? Well, we use a keyword called yield. For example, the following (very simple and not terribly useful) function accepts a block of code and then simply runs it once:
def do_once
yield
end
do_once {puts "hello"}
=> "hello"
do_once {10+10}
=> 20
You can call yield as many times as you like. This is how the Array.each method works, by iterating over the array and calling yield for each item, passing the item as a parameter. Here's a silly example:
def do_thrice
yield
yield
yield
end
do_thrice {puts "hello"}
=> "hello hello hello"
do_thrice {puts "hello".upcase}
=> "HELLO HELLO HELLO"
A block is an unnamed function and you can easily pass parameters to it. The following example accepts an array of names and for each one sends a greeting to the block. The block in this case just puts the greeting to the screen.
def greet(names)
for name in names
yield("Hi There #{name}!")
end
end
greet(["suzie","james","martha"]) { |greeting| puts greeting }
=> Hi There suzie!
Hi There james!
Hi There martha!
We can tell our function to receive a block in two ways:
We can check if a block was passed in using the block_given? method.
This calls the block a specified number of times. It allows you to write code like this:
12.times_over { puts "starfish" }
Recreate this method in your own words.
Extend it so you can call it like this:
12.times_over_with_index { |i| puts "#{i} - starfish" }
This method calls its block once for each element in an array. You call it like this:
[1,2,'cats'].each_with_index { |el, i| puts "#{i} - #{el}" }
Check out the Rails link_to method. Like many helpers it receives an optional block.
Read through the codebase and see if you can see how it does it:
A gem is a zip file containing code, typically, though not always Ruby code, plus a little metadata to help RubyGems and Bundler to manage it nicely. Gems contain a gemspec manifest file, which contains metadata, and usually a lib directory containing the code itself.
Because a gem contains Ruby code, it can do whatever you like. It can monkeypatch Rails. It can delare new modules and classes. It can extend existing objects, etc.
A gem can be stored locally, or on a gem host like rubygems.org. If you publish your gem to rubygems it will be available for anyone to download and use. If you keep it in your project, you can still use bundler, you just provide a path.
http://asciicasts.com/episodes/245-new-gem-with-bundler
In this section we're going to create a simple gem using Bundler.
We are going to create a summarise gem which will extend the string class with methods to create a summary, and to check whether the string can be summarised, like so:
class String
def summarise(l=200)
i = 0
self.split.map{ |word| word if (i += word.length) < l}.compact.join(' ')
end
def summarisable?(length=200)
return self.summarise(length) != self
end
end
first of all create the gem:
bundle gem summarise
We now have a new summarize directory containing a lib directory for code, a gemspec file for metadata, and a few other files.
We also have an empty git repository initialised for us.
The gemspec file is the heart of your gem, it contains all the metadata about your gem. Your generated gemspec will look something like this:
# coding: utf-8
lib = File.expand_path('../lib', __FILE__)
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
require 'summarise/version'
Gem::Specification.new do |spec|
spec.name = "summarise"
spec.version = Summarise::VERSION
spec.authors = ["Nicholas Johnson"]
spec.email = ["email@domain.com"]
spec.description = %q{TODO: Write a gem description}
spec.summary = %q{TODO: Write a gem summary}
spec.homepage = ""
spec.license = "MIT"
spec.files = `git ls-files`.split($/)
spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
spec.require_paths = ["lib"]
spec.add_development_dependency "bundler", "~> 1.3"
spec.add_development_dependency "rake"
end
Notice how spec.files is set by getting the files currently included in git.
Also notice how the version is taken from the Summarize::VERSION constant. This is defined in lib/summarise/version.rb. The version can be updated by editing this file.
We now define the gem in the lib directory.
lib/sumarise.rb
This file simply imports the rest of the code.
require "summarise/version"
require "summarise/string_extensions"
require "summarise/string"
module Summarise
end
lib/summarise/string_extensions.rb
This declares the methods we want to add.
module Summarise
module StringExtensions
def summarise(l=200)
i = 0
self.split.map{ |word| word if (i += word.length) < l}.compact.join(' ')
end
def summarisable?(length=200)
return self.summarise(length) != self
end
end
end
lib/summarise/string.rb
This extends the String class.
class String
include Summarise::StringExtensions
end
Because the gemspec uses Git to discover which files to include, we must first commit your updated files.
git add .
git commit -a -m "first commit"
Build the gem using the gem build command:
gem build summarise.gemspec
You will create a file called something like: summarise-0.0.1.gem
If you want to keep your gem private, you can deploy it directly into your vendor/gems directory, like so:
First unpack it into vendor/gems:
gem unpack summarise-0.0.1.gem --target /path_to_rails_app/vendor/gems/.
Now declare it in your Gemfile:
gem 'summarise', :path => "#{File.expand_path(__FILE__)}/../vendor/gems/summarise-0.0.1"
Finally install it into Gemfile.lock
bundle install
This is a good way to develop a gem, as you can deploy it locally and work on it in situ.
If you'd like to share your gem with the community, you can also push it to RubyGems.org
You'll need a RubyGems account:
https://rubygems.org/users/new
Now simply push the packaged gem:
gem push summarise-0.0.1.gem
Create a random string gem. I want to be able to call something like:
String.random(6)
to get back a random alphanumeric string.
Send allows you to call a method identified by a symbol.
My app has classes for people and events. Each class has different attributes.
class Person
attr_accessor :name
end
class Event
attr_accessor :title
end
I also have a module which sets up the objects.
module Setup
def init(name_or_title)
end
end
Include the Setup module in the Person and Item classes. Now write the init method. use respond_to? and send to initialise either the name or title.
Start with an array [:name, :title], then iterate over it, checking if the object responds to the methods, then call the method when you get a result.
We use define method to create a method on the fly given a string as a name.
class A
define_method :c do
puts "Hey!"
end
end
A.new.b
A.new.c(1,2)
Hamsters come in red, green, blue and orange.
A hamster class looks like this:
class Hamster
attr_accessor :colour
end
for now we'll just assume that only these colours are allowed.
Given an instance of hamster, I would like to be able to call something like:
hammy = Hamster.new
hammy.colour = :red
hammy.is_red?
and have it return true or false
Rather than defining a limited set of methods in advance, use method_missing to catch when a method is not defined, then define it on the fly. If I call is_taupe? method_missing should catch that, define a Hanster#is_taupe? method, then call it.
The following real code is full of duplication. How might you use define_method to dry it up?
class Widget
def product
product = Product.find_by_slug(object_slug)
if !product
product = Product.first
end
product
end
def poem
poem = Poem.find_by_slug(object_slug)
if !poem
poem = Poem.first
end
poem
end
end
If you don't want to create a whole Rails instance, you can use a simple scaffold like this:
class Product
class << self
def find_by_slug
return "success"
end
end
end
class Poem < Product; end
Trying to call a method that doesn't exist in Ruby is an exception rather than a language error. We can catch it, or we can simply implement a method_missing function which will be called when no matching method is found.
class A
def ary
[:a,:b,:c]
end
def method_missing(method, *args)
puts ary.include?(method)
end
end
a = A.new
a.b
a.d
Assuming you have built a mighty warship, give your class an array of abilities like this: [:warp, :photon_torpedos, :holodeck] etc.
Now we're going to use method_missing to allow us to query our space ship. We will be able to call methods like enterprise.has_holodeck?, and voyager.can_warp? and get back a true or a false value.
Here's a regex that should help
FEATURE_REGEX = /^(?:has|can)_(w*)?$/
if find = method.to_s.match(FEATURE_REGEX)
feature = find[1]
end
For a real world example, read and understand the string_enquirer codebase here:
We use instance eval to evaluate a block in the context of a Ruby object.
class A
def initialize
@b = 123
end
end
puts A.new.instance_eval { puts @b }
We can use this to construct a DSL (Domain specific language, like this:)
class Review
attr_accessor :stars, :title, :content
def initialize &block
self.stars = 0
instance_eval &block
end
def set_title t
self.title = t
end
end
r = Review.new do |review|
set_title "new Macbook"
end
Use instance_eval to define a DSL for creating web pages.
Page.new do
set_title "My page"
set_content "Page Content"
end
Extend your DSL so you can also create sub-pages, like so:
Page.new do
set_title "My homepage"
set_content "Page Content"
sub_page do
set_title "My sub page"
set_content "Page Content"
end
end