Demystifying Linked Lists / Complete tutorial/implementation with TDD

This text is not recommended for

  • People who have solid knowledge of Data Structures and Linked Lists
  • People who don’t know ruby and object oriented programming basics

What I’ll explain on this post

  • What is Linked Lists
  • What is Dubly Linked Lists
  • How to make our Linked List Gem
  • Benchmarking

Code

All the code written with this post you can find here

Introduction

On this post I’ll start a serie of posts talking about data structures implementations using Ruby. Some years ago I studied Data Structures but coding each one in C, I think C is a good language to learn because of pointers and struct, but implement in Object Oriented Language like Ruby is good for learn new concepts, plus I’ll use TDD to train and learn more about MiniTest (Because on my day/job I always use Rspec).

What is Linked List?

Linked List is the simplest data structure in my opinion, it consist in set of information/data linked by pointer/link to the next data, the object/struct that stores the data if often called Node/Item.

Linked List

On this image we can see a list of nodes storing values linked by next_node arrow.

But on this tutorial we’ll study doubly linked list that we’ll see below.

Doubly Linked List

Doubly Linked List is the same as Liked List, the unique major difference is that doubly have one pointer to the next node and one to the previous.

DoublY Linked List

Now we have all the information that we need to start coding.

Appplication’s Architeture

Doubly linked list is a simple data structures project, we’ll create a gem that implements it. We’ll have two core classes, linked_list and Node.

Developing

Creating a Gem

On this tutorial we’ll build our linked list in a gem like application, that’s why with gem we can easily build and use the list just including gem in our IRB/Pry session.

So, first we need to create our application, for this just use the command:

bundle gem linked_list

After this, bundler will ask if we want to generate test with our gem, and if yes what gem we prefer, Rspec or Minitest, as I talked above on this tutorial we’ll use Minitest so just write minitest and press enter. Next all that ruby ask, you can say yes and everything will be ok.

After all steps you’ll see ruby creating our gem first files.

      create  linked_list/Gemfile
      create  linked_list/lib/linked_list.rb
      create  linked_list/lib/linked_list/version.rb
      create  linked_list/linked_list.gemspec
      create  linked_list/Rakefile
      create  linked_list/README.md
      create  linked_list/bin/console
      create  linked_list/bin/setup
      create  linked_list/.gitignore
      create  linked_list/.travis.yml
      create  linked_list/test/test_helper.rb
      create  linked_list/test/linked_list_test.rb
      create  linked_list/LICENSE.txt
      create  linked_list/CODE_OF_CONDUCT.md

On this tutorial I’ll not exaplain the basic of a gem architeture, if you’re courius about this project structure go and search for yourself but it’s not really necessary if you know object oriented programming basics

After this, you have a linked_list directory created, now if you try to run any command to your gem, like rake test, it won’t run. Why? Because you will get the fallowing error:

Please fix this gemspec. (Gem::InvalidSpecificationException)
The validation error was '"FIXME" or "TODO" is not a description'

If you already created a gem before you know what it is, is only the rubygems telling you that our description is not completed at file linked_list/linked_list.gemspec. Now let’s fix quickly this.. Open this file and replace the summary and description at line 12 and 13 for anything you want.

  spec.summary       = %q{Linked List}
  spec.description   = %q{Just another one linked list gem that I've coded for study}

But now if you run your tests with rake test, it will not pass yet, because rubygems generates a fail test on our linked_list/test/linked_list_test.rb, if you run test you’ll get the fallowing error:

LinkedListTest#test_it_does_something_useful [/home/otavio/Documents/blog/linked_list/test/linked_list_test.rb:9]:
Expected false to be truthy.

So, open this file and remove the fallowing test:

  def test_it_does_something_useful
    assert false
  end

After this, everything is ok, just run rake test and everything is ok for we start coding.

Creating Node Class

Now that we have our gem created, let’s create our first class, this class we’ll be our node class.

First of all let’s start with test, creating our test and writing the basic structure of a minitest test.

test/node_test.rb

# frozen_string_literal: true

require "minitest/autorun"

class NodeTest < Minitest::Test
end

Now, where to start? I like to start creating a test to assert that we can create our Node class.

  def test_creating_node
    node = Node.new()

    assert_instance_of Node, node
  end

Now if you run this test rake test you’ll get the fallowing error:

  1) Error:
NodeTest#test_creating_node:
NameError: uninitialized constant NodeTest::Node

This error is telling that Ruby didn’t find Node class, to fix this let’s create our class and run test again.

lib/node.rb

# frozen_string_literal: true

class Node
end

And don’t forget to add require "node" at linked_list.rb to don’t get errors when trying to using our gem

Now yoour test should pass right? No, if you try to run your test you’ll get the same error:

  1) Error:
NodeTest#test_creating_node:
NameError: uninitialized constant NodeTest::Node

But you know why? Because we missed to require our node class in our NodeTest, some people aren’t used to require class in Ruby because Rails does it automatically, but pure Ruby no. So go to our NodeTest class and require our Node class:

lib/node.rb

require "node"

Run again your tests, finally we’ll get our test passing.

Now we need to create tests to assert that our class have attributes and can store and returning each one. (If you are familiar with languages like java, this test is to assert our getters/setters).

Let’s create a test to assert that our node are storing and returning its attribute value.

  def test_node_value
    value = 10
    node = Node.new(value: value)

    assert_equal node.value, value
  end

Run tests again, you’ll get the fallowing error telling that we sent one value to the node constructor and it was expecting 0.

  1) Error:
NodeTest#test_node_value:
ArgumentError: wrong number of arguments (given 1, expected 0)

But.. Why is our linked list expecting 0? Because we don’t have a constructor yet, let’s do this.

lib/node.rb

  attr_reader :value

  def initialize(value: nil)
    @value = value
  end

Dont forget to add the attr_reader do make getter method

If you run again your test it will pass, with this class now we have a node storing a value, if we build and install our gem now with rake install we’ll see the fallowing response:

linked_list 0.1.0 built to pkg/linked_list-0.1.0.gem.
linked_list (0.1.0) installed.

Now our gem are builded and installed on our system and we can open our IRB and do the fallowing commands:

require "linked_list"

Node.new(value: 12)
Node.new(value: 10)
Node.new(value: 05)

And we’re creating nodes, but this nodes isn’t linked between each other, they’re just in our memory storing values.

Nodes not linked

Now we need to create our links between each node, this links will be our next node and previous node, as you already know, let’s start with test.

  def test_next_node
    node2 = Node.new(value: 13, next_node: nil)
    node = Node.new(value: 12, next_node: node2)

    assert_equal node.next_node, node2
  end

  def test_previous_node
    node2 = Node.new(value: 13, previous_node: nil)
    node = Node.new(value: 12, previous_node: node2)

    assert_equal node.previous_node, node2
  end

I gave myself the freedom os write both tests because each one is very similar

Now just run your test and see the error:

1) Error:
NodeTest#test_next_node:
ArgumentError: unknown keyword: next_node

 2) Error:
NodeTest#test_previous_node:
ArgumentError: unknown keyword: previous_node

Now, like the error is telling, we need to add next_node and previous_node to our node constructor, and attr_accessor for each one. (We need attr_accessor on each one because we’ll need to set these previous and next nodes)

class Node
  attr_reader :value
  attr_accessor :previous_node, :next_node

  def initialize(value: nil, previous_node: nil, next_node: nil)
    @value = value
    @previous_node = previous_node
    @next_node = next_node
  end
end

Our tests are passing now, to test our nodes with previous and next node, just run again "rake install" to install our builded gem and run IRB requiring "linked_list":

2.4.1 :001 > require "linked_list"
 => true
2.4.1 :002 > node1 = Node.new(value: 12)
 => #<Node:0x00561c467f07c0 @value=12, @previous_node=nil, @next_node=nil>
2.4.1 :003 > node2 = Node.new(value: 10)
 => #<Node:0x00561c467dd1c0 @value=10, @previous_node=nil, @next_node=nil>

Now if we imagine, we’ll have something like this:

Two unlinked nodes

Now with our previous_node and next_node we can link each node forming two interconnected nodes.

2.4.1 :004 > node1.next_node = node2
 => #<Node:0x00561c467dd1c0 @value=10, @previous_node=nil, @next_node=nil>
2.4.1 :005 > node2.previous_node = node1
 => #<Node:0x00561c467f07c0 @value=12, @previous_node=nil, @next_node=#<Node:0x00561c467dd1c0 @value=10, @previous_node=#<Node:0x00561c467f07c0 ...>, @next_node=nil>>

Now if we run node2.previous_node ruby will return node1 and if we run node1.next_node ruby will return node2.

Two linked nodes

We’ve finished our Node logic, now we need to start working on List logic.

Creating LinkedList Class

Follow my blog to get notified every new post:

We created all Node logic and we’ve free way to start creating all list logic, but you can ask me, what’s logic?

  • First Node, a method called .first_node to return the first node of the list.
  • Last Node, a method called .last_node to return the last node of the list.
  • Add Nodes, for this logic I want that our list have a method called .add().
  • Remove last node, create a .pop method, similar to Array#pop from Ruby.
  • Remove first node, create a .shift method, similar to Array#shift from Ruby.
  • Add first node, method called .unshift, similar again to Array#unshift.
  • Print our list, I want a method that I can call with .print method and receive a graphical output on my terminal of my list.

So let’s start defining our head of list (first node)

First Node of the List

Starting from the beginning, we need to store the first node of our list in a var of our LinkedList class, I’ll name this variable head because this will be the "head" of our list.

Starting from the test, let’s create a test that assert that we’re storing the head of the node when creating our list.

Create file test/list_test.rb

# frozen_string_literal: true

require "minitest/autorun"
require "node"

class ListTest < Minitest::Test
  def test_list_head
    node = Node.new(value: 123)
    list = List.new(head: node)

    assert_equal list.head, node
  end

  def test_list_head_setter
    node = Node.new(value: 123)
    list = List.new
    list.head = node

    assert_equal list.head, node
  end
end

Running our test now, we’ll get the fallowing errors:

  1) Error:
ListTest#test_list_head:
NameError: uninitialized constant ListTest::List

This error is simple, is telling that we don’t have a class named List, to fix this just create List class.

lib/list.rb

# frozen_string_literal: true

class List
end

Dont forget to require "list" at our lib/linked_list.rb

Running our tests again:

  1) Error:
ListTest#test_list_head:
ArgumentError: wrong number of arguments (given 1, expected 0)

As we already know, this error is telling that we don’t have a constructor on our class, so let’s create it and test will pass.

Note the use of attr_accessor, with this, the user don’t need to pass the node when creating list, and can write list.head = node, satisfying the second test.

class List
  attr_accessor :head

  def initialize(head: nil)
    @head = head
  end
end

Now running our test, everything will pass, if you want you can install our gem and test this, everything will be working.

Adding Node

Check my last posts:

We need to create a .add() method as I had described before… Starting from the test, what we need to test? I think a good point to start is asserting that we are calling the .add(value) method and we’re adding the new node at the end of the list (pointing the previous las node of th e list to this new last_node), and remember that if list is empty when we add node, this node will be the head node, on this test we’ve a lot of context but we’re not working with rspec so, we will write a lot of test.

Starting from the beginning, we need to assert that inserting a node in a empty list, this node will be the head.

Pay attention that this method will receive a value, not a node.

  def test_add_node_on_empty_list
    list = List.new
    list.add(12)

    assert_equal list.head.value, 12
  end

Now, we need to create the .add(value) method in the list and asserting that if list is blank, the head needs to be the node created from the passed value, this will be easy, is just check if head is nil, if is nil, create a node with value and stores at head.

  def add(value)
    self.head = Node.new(value: value) if head.nil?
  end

Running our test, it will pass but we need to add nodes when our list is not empty.. and how to do that? First we need to pass between each node until find the last node (to find the last node would just check if next_node is nil), and when we find the last node we add the new node after it. Creating a method to return the last node it will look like this:

  def last_node
    last_node = head

    until last_node.next_node.nil?
      last_node = last_node.next_node
    end

    item
  end

In a graphic view:

Searching last node algorithm

But, what’s problem with that method of find the last node? It’s that this method we’re passing between each node, and this is a O(N) algorithm (Don’t know what I’m talking about? Read my post when I tech Big O with simple approach here), ok O(N) isn’t a big problem with this example in image of four nodes but with a lot of nodes in our list, but with something like thousands of nodes it will be a problem.

To solve this problem, we have a good solution in this case, it’s to create a last_node and everytime that we add a new node, we store it at last_node of the list, with this, when we add a new node, we check the current last_node of the list and stores the new node after this and set last_node of the list again, with this simple solution we will not have problems adding new nodes for any size of list. On this post we’ll follow this approach.

Starting from the beginning..

Note that we’ll name last node of the list as tail.

Creating the test and the method for the tail. (I’ll skip some steps here because is the same as head).

  def test_list_tail
    node = Node.new(value: 123)
    list = List.new(tail: node)

    assert_equal list.tail, node
  end

  def test_list_tail_setter
    node = Node.new(value: 123)
    list = List.new
    list.tail = node

    assert_equal list.tail, node
  end

And the code:

  attr_accessor :head, :tail

  def initialize(head: nil, tail: nil)
    @head = head
    @tail = tail
  end

Backing to our add method, let’s create the test.

  def test_add_node
    list = List.new

    for number in 1..10 do
      list.add(number)
    end

    list.add(99)

    assert_equal list.tail.value, 99
  end

Running this test, obviusly it will fail, and now we need to complete our .add method, at this point we need to store the tail of our list. We have two context, one when list only have one node, this node will be the head and the tail of our list, and the second context when list have more than one node and we need to change the tail to the new node.

I’ll comment the fallowing code to be more clean on my explanation, but please, don’t make your code with comments like these.

  def add(value)
    new_node = Node.new(value: value) # Creating our Node

    # Checking if is the first Node of the list
    self.head = new_node if head.nil?
    self.tail = new_node if tail.nil?

    # Making the new node the tail of the list

    # The previous node of the new node is the current tail
    new_node.previous_node = self.tail

    # The next node of the current tail is the new node
    self.tail.next_node = new_node

    self.tail = new_node # Set new node as tail
  end

Note that we created the new_node to have the same node in all parts.

Adding new node

Run your test again and everything will be working, you can install your gem (as we done previously) and test if you want.

.pop method

Ok, the most difficult part already gone, now we need to create simple methods to interact with our list, starting from .pop method. As I described at the beginning of chapter:

  • Remove last node, create a .pop method, similar to Array#pop from Ruby.

This method is very similar to .add but we’ll remove the last node, in Ruby we’ve a garbage collector so everything we need to do is to set de previous_node of tail as new list tail and set his next_node to nil.

Implementing this in languages that doesn’t have garbage collector like C you’ll need to free the node that you’re removing.

Removing Node

Starting from Test, it just need to assert that after .pop the new tail is the expected.

  def test_list_pop
    list = List.new

    for number in 1..10 do
      list.add(number)
    end

    list.pop

    assert_equal list.tail.value, 9
  end

Obviusly it will fail because we don’t have the method, creating the method, we need that this method picks the previous_node of current list tail and sets it as new tail, after that point his next_node to nil, pretty simple isn’t it?

Again I’ll use comments but I repeat is just for learning purpose.

  def pop
    old_tail = self.tail # Saving the old tail

    # Picking the previous node of the old_tail to be the new tail
    self.tail = old_tail.previous_node
    # Pointing the new tail to nil
    self.tail.next_node = nil

    # Setting the previous_node of old_tail to nil to completely isolate it
    old_tail.previous_node = nil
  end

Note that on the last line of the method I set old_tail.previous_node = nil to completely isolate it from the rest of the list, facilitating for garbage collector of ruby eliminate it.

Run our test again and everything will pass, signaling that our .pop is complete.

.shift method

This method will be very similar to .pop but as I’ve described we’ll remove the head of the list.

  • Remove first node, create a .shift method, similar to Array#shift from Ruby.

Everything we need to do is to set the next_node of the current head to be the new head!

Removing Head

Test:

Just need to assert that after .shift the new head is the expected.

 def test_list_shift
    list = List.new

    for number in 1..10 do
      list.add(number)
    end

    list.shift

    assert_equal list.head.value, 2
  end

Now to make this test pass we need to create the .shift method. It is simple, is the same thing as the .shift just reversing the sides, look:

def shift
    old_head = self.head # Saving the old head

    # Picking the next node of the old_head to be the new head
    self.head = old_head.next_node

     # Pointing the new head previous_node to nil
    self.head.previous_node = nil

    # As I explained above, it's for garbage collector
    old_head.next_node = nil
  end

Everything is the same, if you understand one, automatically you understand another.

Run your test now and everything will pass.

.unshift method

As I described, now we’ve to create the .unshift method.

  • Add first node, method called .unshift, similar again to Array#unshift.

This method is very similar to .shift, but instead of move the head we’ll add a new head on the list.

How we do that? It’s simple, we create the node that will be the new head and point his next_node to the old head, sets old head previous node to the new node and after this set list head as the new node, pretty simple.

This test will be very simple, is just assert that after .unshift(value) our list head is this value.

  def test_list_unshift
    list = List.new

    list.add(10)
    list.add(20)
    list.add(30)
    list.add(50)

    list.unshift(5)

    assert_equal list.head.value, 5
  end

Running tests, obviusly it will fail because we don’t have .unshift() method created. Creating our method, it will be very similar, if you understand .shift and .pop you will easily understand this.

  def unshift(value)
    new_head = Node.new(value: value)

    old_head = self.head

    old_head.previous_node = new_head
    new_head.next_node = old_head

    self.head = new_head
  end

If you run your test it will pass and if you want you can install your gem and test it by using.

Note I’m not installing gem and testing each time we create a method because I’ll test everything on the final of post, feel comfortable to install and test if you want.

Printing our List

As I described in the beginning of the chapter.

  • Print our list, I want a method that I can call with .print method and receive a graphical output on my terminal of my list.

This will be very funny and useful, it will help us use our list more easy and understand what is happening when working with the list.

At this method we have a lot of solutions and print designs possible, but for this test I’ll use a very simple design, I want that when we call list.print the output looks like this:

12 <-> 23 <-> 45 <-> 12 <-> 56

Just each node pointing to the next/previous by an arrow.

Starting from the test, we need to create a test that assert the output of `.print` with a string of the expected output.

  def test_list_print
    list = List.new

    list.add(10)
    list.add(20)
    list.add(30)
    list.add(50)

    assert_equal list.print, "10 <-> 20 <-> 30 <-> 50"
  end

Pay attention that on head we dont have previous arrow, and at the tail we dont have next arrow.

Running our test and failing it, we need to create our method, for this method we’ve a lot of ways of create this, one simple way that I think is good is to stores every node value at one array and after that join the array with <-> and it will pass, the code will looks like this:

  def print
    node = self.head
    output = []

    until node.nil?
      output << node.value

      node = node.next_node
    end

    output.join(" <-> ")
  end

This solution is very simple, we pass between each array, stores and after this, joins with arrows. If you run your test it will pass.

This solution is good and simple, I know that for this problem we’ve a lot of solutions and I’ll love to learn more, if you have another idea, please comment, but for this post we’ll continue with this.

Another option is to include Enumerable module and pass between each node printing the arrows.

Bonus Methods

Implementing a .each method, and after this including Enumerable module, we can have access to all power of Enumerable provide to us, it includes .each, .map, .select and much more.

Don’t know what I’m talking about? I’ve made a post/tutorial explaining it, check out here.

Related Posts

Add include Enumerable at the beginning of our class, and implement it each method.

A nice test will be assert that our list is a kind_of? Enumerable

  def test_list_each
    list = List.new

    assert_kind_of Enumerable, list
  end

And the method..

  def each
    node = self.head

    until node.nil?
      yield node
      node = node.next_node
    end
  end

Manual Testing our Linked List

Ok, now everything we need to do is test, we already have .print method, so test, will be very funny.

Installing our gem with rake install and opening our IRB and requiring our gem with require "linked_list" we can start playing with our project.

2.4.1 :001 > require "linked_list"
 => true

And if we create our list, I should have nil head and tail.

2.4.1 :002 > l = List.new
 => #<List:0x005556db27b708 @head=nil, @tail=nil>
2.4.1 :003 > l.head
 => nil
2.4.1 :004 > l.tail
 => nil

And now if we add something it should be the head and tail of our node.

2.4.1 :005 > l.add(23)
# ...
2.4.1 :006 > l.head.value
 => 23
2.4.1 :007 > l.tail.value
 => 23

Fine, is working as expected, now let’s add a lot of nodes on our list.

2.4.1 :008 > l.add(324)
2.4.1 :009 > l.add(10)
2.4.1 :010 > l.add(15)
2.4.1 :011 > l.add(20)
2.4.1 :012 > l.add(100)

Now we have 6 nodes on our list, and this is perfect to test our print method.

2.4.1 :013 > l.print
 => "23 <-> 324 <-> 10 <-> 15 <-> 20 <-> 100"

Ok, print is working, now we need to test .pop, .shift, .unshift() and our bonus methods (Enumerable).

Testing on that order:

2.4.1 :014 > l.pop
 => nil
2.4.1 :015 > l.print
 => "23 <-> 324 <-> 10 <-> 15 <-> 20"
2.4.1 :016 > l.shift
 => nil
2.4.1 :017 > l.print
 => "324 <-> 10 <-> 15 <-> 20"
2.4.1 :018 > l.unshift(10000)
# ...
2.4.1 :019 > l.print
 => "10000 <-> 324 <-> 10 <-> 15 <-> 20"

# Testing Enumerable

2.4.1 :020 > l.each do |node|
2.4.1 :021 >   puts node.value
2.4.1 :022?> end
10000
324
10
15
20

We finished our tests, feel comfortable to test anything you want.

Finishing

Now we got our list finished and working and I hope that you have learned a lot with this post. I love to implement data structures because I think that we can learn a lot doing these simple exercises, In the next post I’ll talk about ordered datasets and sorting and search algorithms and I think it will be very fun. (depending on when you are reading this, probably I already wrote that post, you can check here)

Remembering, that the code of this is list you can find on my github here

And if you have any question please ask anywhere, you can comment this post, PM me on twitter or send me an email (otaviopvaladares@gmail.com), I’ll love it!

Don’t forget to follow me on my twitter

Have a great studies!

Follow my blog to get notified every new post:

each, map, select in ruby? Understanding Enumerable

When you’re starting with ruby programming, a lot of people (and me too), usually have questions about the difference between ruby .map, .select, .collect, .each and many others enumerables.

Each

Each executes for each element the logic passed in block.

For example, if you want to print each element of one array using what we usually do in another languages with for loops, it will look like this:

places = ["restaurant", "mall", "park", "theater"]

for i in 0..places.size
puts places[i]
end

# => restaurant
# => mall
# => park
# => theater

Ok, this works but is not readable, it’s not a natural language, and in ruby, we love to prioritize it, with .each it will be much more lovely.

places.each do |place|
  puts place
end

# => restaurant
# => mall
# => park
# => theater

Or if you want or are working on IRB you can use inline syntax.

places.each { |place| puts place }

Another Example

numbers = [1, 2, 3, 4, 5]
numbers.each { |number| puts number * 2 }

# => 2
# => 4
# => 6
# => 8
# => 10

Pay attention, each doesn’t alter the array.

Note, on last example we multiply each element by 2, but if we print, the array is not multiplied

puts numbers

# => 1
# => 2
# => 3
# => 4
# => 5

Note too, if we try to save each block at one variable it doesn’t work, because it will return the same array

a = numbers.each { |number| puts number * 2 }
# ..
puts a
# => [1, 2, 3, 4, 5]

Map

Different from each, the .map is very powerful because it returns an array with the results of the block.

Different from each, the .map is very powerful because it returns an array with the results of the block.

Note, the final output is an array

numbers.map { |number| number * 2 }

# => [2, 4, 6, 8, 10]

More understandable example:

On this example we apply .upcase on each element, after this we’ve an array places_upcase with each place in upcase.

places = ['restaurant', 'mall', 'park', 'theater']
places_upcase = places.map { |place| place.upcase }
# => ["RESTAURANT", "MALL", "PARK", "THEATER"]

places_upecase
# => ["RESTAURANT", "MALL", "PARK", "THEATER"]

Collect

Collect is the same as .map it’s just an alias.

Select

Also Known As "Filter" in other languagens, select executes the passed expression for each element of array, if is true, it adds to the final response array, for example:

On this code we use select to return an array with all numbers greater than 3.

numbers = [1, 2, 3, 4, 5]

numbers.select { |number| number > 3 }
# => [4, 5]

Another good example is to use select to get all odd numbers of one array.

odd_numbers = numbers.select { |number| number.odd? }

odd_numbers
# => [1, 3, 5]

Behind the scnenes

Check my last posts:

map, select, collect isn’t all, you have over 50 methods in Enumerable mixin.

Now you can ask me, what is this Enumerable mixin? It’s simple.

Enumerable it’s a ruby module, it provides collection classes with several traversal and searching methods, a lot of methods, as you can see:

Enumerable.instance_methods
# => [:to_a, :to_h, :include?, :find, :entries, :sort, :sort_by, :grep, :grep_v, :count, :detect, :find_index, :find_all, :select, :reject, :collect, :map, :flat_map, :collect_concat, :inject, :reduce, :partition, :group_by, :first, :all?, :any?, :one?, :none?, :min, :max, :minmax, :min_by, :max_by, :minmax_by, :member?, :each_with_index, :reverse_each, :each_entry, :each_slice, :each_cons, :each_with_object, :zip, :take, :take_while, :drop, :drop_while, :cycle, :chunk, :slice_before, :slice_after, :slice_when, :chunk_while, :lazy]

Enumerable.class
# => Module

Wow, it’s awesome, isn’t it?

And for being a module, you can include it on your class and have access to this collection of powerful methods. But, how to include it? It’s not just add include Enumerable to your class, you need to create an each method in your class that yields every element of collection.

As described in ruby documentation: "The class must provide a method each, which yields successive members of the collection."

Let’s use a simple example to apply this, a class named OddNumbers.

The class have a each method that yields three odd numbers.

class OddNumbers
  include Enumerable

  def each
    yield 1
    yield 3
    yield 5
  end
end

After do that, you already can see all Enumerable methods in your class.

OddNumbers.instance_methods
# => [:each, :to_a, :to_h, :include?, :find, :entries, :sort, :sort_by, :grep, :grep_v, :count, :detect, :find_index, :find_all, :select, :reject, :collect, :map, :flat_map, :collect_concat, :inject, :reduce, :partition, :group_by, :first, :all?, :any?, :one?, :none?, :min, :max, :minmax, :min_by, :max_by, :minmax_by, :member?, :each_with_index, :reverse_each, :each_entry, :each_slice, :each_cons, :each_with_object, :zip, :take, :take_while, :drop, :drop_while, :cycle, :chunk, :slice_before, :slice_after, :slice_when, :chunk_while, :lazy, :instance_of?, :public_send, :instance_variable_get, :instance_variable_set, :instance_variable_defined?, :remove_instance_variable, :private_methods, :kind_of?, :instance_variables, :tap, :method, :public_method, :singleton_method, :is_a?, :extend, :define_singleton_method, :to_enum, :enum_for, :<=>, :===, :=~, :!~, :eql?, :respond_to?, :freeze, :inspect, :display, :object_id, :send, :to_s, :nil?, :hash, :class, :singleton_class, :clone, :dup, :itself, :taint, :tainted?, :untaint, :untrust, :trust, :untrusted?, :methods, :protected_methods, :frozen?, :public_methods, :singleton_methods, :!, :==, :!=, :__send__, :equal?, :instance_eval, :instance_exec, :__id__]

And you can see it as included module too.

OddNumbers.included_modules
# => [Enumerable, Kernel]

Note, include Enumerable module without provide each method in your class, it won’t works.

And this is why ruby’s Array and Hash have this methods, on their code, they include Enumerable.

Array.included_modules
# => [Enumerable, Kernel]
Hash.included_modules
# => [Enumerable, Kernel]

Conclusion

A lot of people when are starting with ruby programming think all these methods it’s all the same (and it’s normal!) but they aren’t, when you understand each one, not only each/select/map but a lot of others of Enumerable module, you have great power in your fingers, obviously nobody will memorize all methods of this module, but you can search on documentation when you need something that smeels like an Enumerable method.

If you have any question that I can help you, please ask! Send email (otaviopvaladares@gmail.com), pm me on my twitter or comment this post!

This post was originally written in portuguese by me, here

Follow my blog to get notified every new post:

Understanding rails basic CoC under scaffold for beginners

Rails is a framework where almost everything is based on convention over configuration (CoC) philosophy. Many beginners with rails framework don’t understand exactly what rails do with CoC to build applications.

This is a tutorial for beginners only, if you’re not new in ruby/rails development, close this tab, I will explain exactly what rails do on a basic CRUD flow based on what rails do when creating a scaffold.

The code used at this tutorial can be found here.

Creating the example

First of all, create and rails application with any name, In this tutorial, I will use the library (With sqlite because of its don’t matter here).

rails new library

(If you usually read my blog, at this point you already realized that I use library example on every tutorial, I will use other examples on my next tutorial, I promise)

Now, you’re with a new application without any controller or model (except application).

Our focus on this tutorial will be on the interaction between model-view-controller while creating a simple CRUD.

At this time, rails already created all project structure and our folders are this:

app
├── assets
├── channels
├── controllers
│   ├── application_controller.rb
│   └── concerns
│       └── .keep
├── models
│   ├── application_record.rb
│   └── concerns
│       └── .keep
└── views
    └── layouts
        ├── application.html.erb
        ├── mailer.html.erb
        └── mailer.text.erb

In my opinion the most simple way to understanding and see what rails do with CoC philosophy is using simple rails command-line tools and see what files rails create and where.

rails g scaffold book isbn:string name:string author:string description:text

Rails will output something like this:

      invoke  active_record
      create    db/migrate/20171225225129_create_books.rb
      create    app/models/book.rb
      invoke    test_unit
      create      test/models/book_test.rb
      create      test/fixtures/books.yml
      invoke  resource_route
       route    resources :books
      invoke  scaffold_controller
      create    app/controllers/books_controller.rb
      invoke    erb
      create      app/views/books
      create      app/views/books/index.html.erb
      create      app/views/books/edit.html.erb
      create      app/views/books/show.html.erb
      create      app/views/books/new.html.erb
      create      app/views/books/_form.html.erb
      invoke    test_unit
      create      test/controllers/books_controller_test.rb
      invoke    helper
      create      app/helpers/books_helper.rb
      invoke      test_unit
      invoke    jbuilder
      create      app/views/books/index.json.jbuilder
      create      app/views/books/show.json.jbuilder
      create      app/views/books/_book.json.jbuilder
      invoke  test_unit
      create    test/system/books_test.rb
      invoke  assets
      invoke    coffee
      create      app/assets/javascripts/books.coffee
      invoke    scss
      create      app/assets/stylesheets/books.scss
      invoke  scss
      create    app/assets/stylesheets/scaffolds.scss

Understading what rails do with Scaffold

At this time, let’s look carefully at everything that rails do on the last command, starting with first lines.

  invoke  active_record
      create    db/migrate/20171225225129_create_books.rb
      create    app/models/book.rb
      invoke    test_unit
      create      test/models/book_test.rb
      create      test/fixtures/books.yml

Rails invoked an active record to generate a migration file with the fields that we specified on scaffold command and created a model book and a test for him.

     invoke  resource_route
     route   resources :books

On the next lines (the above lines), rails created resources routes for books.

Resource routing allows you to quickly declare all of the common routes for a given resourceful controller. Instead of declaring separate routes for your index, show, new, edit, create, update and destroy actions, a resourceful route declares them in a single line of code. See more: http://guides.rubyonrails.org/routing.html#resource-routing-the-rails-default

If you understood what resources do at routes you already realized that rails will search for a controller named book when receiving a request for /books, and that is exactly what rails created on the next steps of scaffold:

    invoke  scaffold_controller
    create    app/controllers/books_controller.rb

But if you open this file, you will see a complete controller with all methods that resources do.

class BooksController < ApplicationController
  before_action :set_book, only: [:show, :edit, :update, :destroy]

  # GET /books
  # GET /books.json
  def index
    @books = Book.all
  end

  # GET /books/1
  # GET /books/1.json
  def show
  end

  # GET /books/new
  def new
    @book = Book.new
  end

  # GET /books/1/edit
  def edit
  end

  # POST /books
  # POST /books.json
  def create
    @book = Book.new(book_params)

    respond_to do |format|
      if @book.save
        format.html { redirect_to @book, notice: 'Book was successfully created.' }
        format.json { render :show, status: :created, location: @book }
      else
        format.html { render :new }
        format.json { render json: @book.errors, status: :unprocessable_entity }
      end
    end
  end

  # PATCH/PUT /books/1
  # PATCH/PUT /books/1.json
  def update
    respond_to do |format|
      if @book.update(book_params)
        format.html { redirect_to @book, notice: 'Book was successfully updated.' }
        format.json { render :show, status: :ok, location: @book }
      else
        format.html { render :edit }
        format.json { render json: @book.errors, status: :unprocessable_entity }
      end
    end
  end

  # DELETE /books/1
  # DELETE /books/1.json
  def destroy
    @book.destroy
    respond_to do |format|
      format.html { redirect_to books_url, notice: 'Book was successfully destroyed.' }
      format.json { head :no_content }
    end
  end

  private
    # Use callbacks to share common setup or constraints between actions.
    def set_book
      @book = Book.find(params[:id])
    end

    # Never trust parameters from the scary internet, only allow the white list through.
    def book_params
      params.require(:book).permit(:isbn, :name, :author, :description)
    end
end

And this is part of rails scaffold magic, he generates the code of the controller, and this only works because of CoC that rails implements.

At this point, we already have the model and Controller, and on the next lines of the scaffold, we will have the views, completing the MVC design.

      invoke      erb
      create      app/views/books
      create      app/views/books/index.html.erb
      create      app/views/books/edit.html.erb
      create      app/views/books/show.html.erb
      create      app/views/books/new.html.erb
      create      app/views/books/_form.html.erb

At this point, rails created all the views that correspond for each action of the books controller, following the specified convention.

Rails documentation of action view for better understanding:

“There is a naming convention for views in Rails. Typically, the views share their name with the associated controller action, as you can see above. For example, the index controller action of the articles_controller.rb will use the index.html.erb view file in the app/views/articles directory. The complete HTML returned to the client is composed of a combination of this ERB file, a layout template that wraps it, and all the partials that the view may reference. Within this guide, you will find more detailed documentation about each of these three components. See more: http://guides.rubyonrails.org/action_view_overview.html&#8221;

Now, everything is done, we have the complete MVC. If you start your applications (you’ll need to run your migrations) and go to `http://localhost:3000/books` you can interact with our scaffold.

Your application should look like this:

(Note that I already added one book)

How rails pass information between controller-gview using CoC


Follow my blog:


But how everything works? How the information pass between the application? Let`s see this.

For this, we will use a legendary method called debug, first of all, add gem byebug at your gemfile.

gem 'byebug', '~> 9.0', '>= 9.0.6'

After this, run bundle at your project folder, wait and let`s debug.

For this test, we will use the index page of books.

But how to find where we can put our byebug? So, run rake routes at your project and you`ll see:

Prefix Verb   URI Pattern               Controller#Action
    books GET    /books(.:format)          books#index
          POST   /books(.:format)          books#create
 new_book GET    /books/new(.:format)      books#new
edit_book GET    /books/:id/edit(.:format) books#edit
     book GET    /books/:id(.:format)      books#show
          PATCH  /books/:id(.:format)      books#update
          PUT    /books/:id(.:format)      books#update
          DELETE /books/:id(.:format)      books#destroy

Now you can see that when we access our application on /books we are redirected to book controller at index action. So, when we need to put our byebug? Exact, on books controller at index action.

class BooksController < ApplicationController
  before_action :set_book, only: [:show, :edit, :update, :destroy]

  # GET /books
  # GET /books.json
  def index
    byebug
    @books = Book.all
  end

Now access our application at /books and go to terminal window when our server is running and we’ll see:

 6:   def index
    7:     byebug
=>  8:     @books = Book.all
    9:   end

Now, on this line, we’re selecting all books using Active Record Book.all and storing at @books variable.

At this point @books is storing all books of my application in a ActiveRecord::Relation (But this don’t matter on this tutorial).

#<ActiveRecord::Relation [#<Book id: 1, isbn: "978-1400096237", name: "The Information", author: "James Gleick", description: "A nice book", created_at: "2017-12-27 19:18:01", updated_at: "2017-12-27 19:21:32">]>

But it’s only this, why are we storing this at @books? Where rails calls the view? Simple, fallowing the convention, our action is books controller, index action (fallowing what are in routes books#index) so rails will search for the view at app/views/books/index.html.erb and automatically starts it passing the @books variable that we created on controller.

So, open the books#index view file, and note that on some line (16 at my rails version), the generated code is accessing @books that we created on controller and using each loop.

<tbody>
    <% @books.each do |book| %>
      <tr>
        <td><%= book.isbn %></td>
        <td><%= book.name %></td>
        <td><%= book.author %></td>
        <td><%= book.description %></td>
        <td><%= link_to 'Show', book %></td>
        <td><%= link_to 'Edit', edit_book_path(book) %></td>
        <td><%= link_to 'Destroy', book, method: :delete, data: { confirm: 'Are you sure?' } %></td>
      </tr>
    <% end %>
  </tbody>

Now if you put another byebug before our loop:

....

<% byebug %>
    <% @books.each do |book| %>
      <tr>

....

And make a new request, and wait for stop at this byebug:

  17:   <% byebug %>
=> 18:     <% @books.each do |book| %>
   19:       <tr>

And calls for @books variable, we’ll see the the same data at variable (obviously).

<ActiveRecord::Relation [#<Book id: 1, isbn: "978-1400096237", name: "The Information", author: "James Gleick", description: "A nice book", created_at: "2017-12-27 19:18:01", updated_at: "2017-12-27 19:21:32">]>

And that’s is how rails pass data between the controller-view. For a better example I will create a new variable with my name at books#index:

def index
    @books = Book.all
    @name = 'Octos'
  end

And I’ll add a new <h2> with this variable at view:

<h1>Books</h1>
<h2><%= @name %></h2>

And make a new request, and I’ll see my name at index because the rails pass the information of @name between the controller-view.

Disclaimer: The variable of controller and view is not the same, you can see this putting two byebugs, one on the controller and another at the view and calling the `object_id` on each, the numbers will be different.

Controller:

(byebug) @books.object_id
8

View:

(byebug) @books.object_id
70132622813300

Finishing

Everything is done, if you have any question, please ask.

The code of the example can be found here.

That’s all for today folks, don’t forget to follow my blog and my twitter.