Understanding Ruby’s idiom: array.map(&:method)

Ruby has some idioms that are used pretty commonly, but not very often understood. array.map(&:method_name) is one of them. We can see it being used everywhere to call a method on every array element, but why this works? What’s really happening under the hood?

In case you don’t know Ruby’s map

map is used to execute a block of code for each element of a given Enumerableobject, like an Array. Here’s an example:

class Foo
  def method_name
    puts "method called for #{object_id}"
  end
end

[Foo.new, Foo.new].map do |element| 
  element.method_name
end

# => method called for 70339841711300
# => method called for 70339841711280

As we are just calling method_name for each element of the list, Ruby allows us to use this idiom:

[Foo.new, Foo.new].map(&:method_name)

What Ruby does when it sees &

The first thing that happens is that, whenever Ruby sees a & for a parameter, it wants this parameter to be a Proc. If this is not the case already, Ruby calls #to_proc on this object to convert it. Let’s confirm this is true:

class MyClass
  def to_proc
    puts "trying to convert to a proc"
    Proc.new {}
  end
end

[].map(&MyClass.new)

# => trying to convert to a proc

If you don’t know what a Proc is, you can consider it to be just like a lambda or a closure. It’s a piece of code that can be moved around and executed (by calling call() on it, for instance).

As we passed a MyClass instance with & to map, it tried to call to_proc on it. This holds true for any method call, not just map.

Back to the previous example, we are calling map with &:method_name. So we know that Ruby will see that & and try to call :method_name.to_proc. The next step is to understand what Symbol#to_proc does.

Symbol’s smart to_proc implementation

What Symbol#to_proc does is quite clever. It tries to calls a method with the same name (in our example, method_name) on the given object.

Maybe an example will make more sense:

:upcase.to_proc.call("string")
# => STRING

When we call to_proc on the :upcase symbol, it will return a Proc object that just call the upcase method for the given parameter (“string”).

Implementing our own version

One of the approaches that I like to take to understand how something works is to create my own dumb implementation of it. After we understand all the building blocks that make this idiom work, this should not be that hard.

First, let’s implement our own map method:

def my_map(enumerable, &block)
  result = []
  enumerable.each { |element| result << block.call(element) }
  result
end

We iterate over the Enumerable object and execute that given block. We know that block is going to be a Proc, because Ruby called to_proc on it, so we can just call it.
And this works.

my_map(["foo", "bar"], &:upcase)
# => ["FOO", "BAR"]

Now let’s implement our own Symbol functionality:

class MySymbol
  def initialize(method_name)
    @method_name = method_name
  end

  def to_proc
    Proc.new do |element|
      element.send(@method_name)
    end
  end
end

We know that we just need to implement the to_proc method that Ruby is going to call and make it return a Proc object.
As this is not really a Symbol, we will define the method to be called in the constructor. The method name is dynamic, so we need to use Ruby’s send to call it.
And this works.

my_map(["foo", "bar"], &MySymbol.new("upcase"))
# => ["FOO", "BAR"]

Summarizing

  • Ruby instanciates a MySymbol object;
  • Ruby checks that there is a & and calls to_proc on this object;
  • MySymbol#to_proc returns a Proc object, that expects a parameter (element) and calls a method on it (upcase);
  • my_map iterates over the received list (['foo', 'bar']) and calls the received Proc on each element, passing it as a parameter (block.call(element));
  • The Proc then executes element.send("upcase"), that is basically the same as "foo".upcase, and will return the expected result.
Advertisements

Print Avery labels using CSS and HTML

<!doctype html>
<html lang="en">
<head>
    <meta charset="utf-8">
    <title>HTML & CSS Avery Labels (5160) by MM at Boulder Information Services</title>
    <style>
    body {
        width: 8.5in;
        margin: 0in .1875in;
        }
    .label{
        /* Avery 5160 labels -- CSS and HTML by MM at Boulder Information Services */
        width: 2.025in; /* plus .6 inches from padding */
        height: .875in; /* plus .125 inches from padding */
        padding: .125in .3in 0;
        margin-right: .125in; /* the gutter */

        float: left;

        text-align: center;
        overflow: hidden;

        outline: 1px dotted; /* outline doesn't occupy space like border does */
        }
    .page-break  {
        clear: left;
        display:block;
        page-break-after:always;
        }
    </style>

</head>
<body>

<div class="label"><img src="https://boulderinformationservices.files.wordpress.com/2011/08/barcode_sample.png" /><br>Human readable</div>
<div class="label"><img src="https://boulderinformationservices.files.wordpress.com/2011/08/barcode_sample.png" /><br>Human readable</div>
<div class="label"><img src="https://boulderinformationservices.files.wordpress.com/2011/08/barcode_sample.png" /><br>Human readable</div>
<div class="label"><img src="https://boulderinformationservices.files.wordpress.com/2011/08/barcode_sample.png" /><br>Human readable</div>
<div class="label">(Repeat 30 times)</div>
<div class="page-break"></div>

</body>
</html>

How To Use RVM to Manage Ruby Installations and Environments

Basic Syntax

If you’ve already installed RVM, you should have the rvm command available to you. This is the way we call RVM.

The basic syntax of the command is:

rvm command_options command ruby_to_act_on

There are also RVM flags that can alter the behavior of RVM, which are given in a similar way to command options.

You can get a list of the available commands by typing:

rvm help

If you would like help on a specific command, you can reference it after “help” to get more detailed instruction:

rvm help command

How To Enable Tab Completion

We will enable RVM tab completion by putting the following line in our .bashrc file:

[[ -r $rvm_path/scripts/completion ]] && . $rvm_path/scripts/completion

This will allow us to complete RVM commands by typing the TAB key twice after entering part of the command. For instance, we can type:

rvm inst

At this point, we can hit TAB twice, and it will complete to:

rvm install

We can then finish typing parameters.

Keep in mind that this also works with arguments. If you are switching to another Ruby version, you can type:

rvm use

After you type a space and then TAB twice, you will be presented with a list of the available Ruby versions.

How To Install and Uninstall Rubies

We can list all of the Rubies we have available to install with this command:

rvm list known

Once you’ve selected the Ruby you wish to install, you can issue this command:

rvm install ruby_version

If you ever wish to uninstall a version of Ruby, you can do this simply by typing:

rvm uninstall ruby_version

How To Switch Rubies

Once you’ve installed a few versions of Ruby, you can list them with this command:

rvm list
rvm rubies

=* ruby-2.0.0-p247 [ x86_64 ]

# => - current
# =* - current && default
#  * - default

As you can see, RVM gives you a handy guide to tell you which are the current and the default Ruby versions. In this case, they are one and the same.

Switch to a different Ruby by typing:

rvm use ruby_version

Set a default Ruby to use by using the --default flag:

rvm --default use ruby_version

To switch to the default Ruby, type:

rvm default

In order to use the version of Ruby installed on the system (not through RVM), you can specify:

rvm use system

How To Use Gemsets

One common way to distribute code in Ruby is to use a format called gems. Gems can be installed to extend the capabilities of the core Ruby distribution, and there are often gems that are required to be installed to get certain programs to function correctly.

In keeping with RVM’s mission of providing contained Ruby environments, it is also possible to install gems that are only associated with a single Ruby installation. RVM calls this functionality gemsets.

This means that you can have two different versions of the same gem, or you can make gems unaware of other gems on the system.

To see the available gemsets for the current Ruby, you can type:

rvm gemset list

If you have more than one Ruby version installed, you can see all of the gemsets by typing:

rvm gemset list_all

By default, you should have two gemsets configured:

  • default: The gemset that is applied if no other gemset is specified.
  • global: This gemset is inherited by every other gemset that is used. This set generally does not need to be selected because it will be included automatically. You should install shared gems here.

You can create another gemset easily. We will create a gemset called “test_project” to demonstrate how this works:

rvm gemset create test_project

If you would rather copy a current gemset to a new gemset to run some tests, you can issue this command:

rvm gemset copy default test_project

We can change the gemset we wish to use:

rvm gemset use test_project

We can also change the Ruby version and gemset at one time. This is done giving the Ruby version, followed by the “@” character, and then specifying the gemset:

rvm use 2.0.0@test_project

Now, we can install a Tic-Tac-Toe gem by issuing this command:

gem install tictactoe -v 0.0.4

We can now change to our default gemset and install an earlier version of the same gem:

rvm gemset use default
gem install tictactoe -v 0.0.3

We now have two separate versions of the Tic-Tac-Toe gem installed and we can test them independently by switching the gemset that we are using.

If you’re confused about which gemset you’re currently working with, this command will print the current active gemset:

rvm gemset name

When you’ve finished using a gemset, perhaps because your testing is complete, you can get rid of it by issuing the following command:

rvm gemset delete test_project

How To Configure Defaults

RVM can be configured with defaults on a few different levels. RVM keeps its defaults in a file here:

nano ~/.rvm/config/db

You can see what RVM will use if you do not give it specific directions to do otherwise.

Note: You should not edit this file. It is overwritten when RVM is upgraded.

If you would like to override these settings, you can do so in a separate file at:

nano ~/.rvm/user/db

For ease of use, you can copy parameters out of the config/db file and place it in the user/db file to modify easily.

How To Automate Your Environment

You can create project-specific configurations that specify what Ruby version and gemset to use by creating an .rvmrc file inside of your project directory.

This eliminates the need to manually keep track of the ruby version you have active.

To create a project-specific environment, just create an .rvmrc file in the project’s top-level directory:

nano .rvmrc

Inside, you just need to type “rvm”, followed by the Ruby version, the “@” symbol, and then the gemset:

rvm ruby_version@gemset

That’s all you need. You may have to accept the configuration the first time you enter the directory.

Ensure that you have created the gemset and installed the Ruby version you are specifying, or else you will be prompted to install and create the necessary components whenever you switch into that directory.

You can also include any kind of project-specific RVM configuration within this same file.

How To Update RVM

When RVM comes out with a new version, you can update your installation easy from within the utility.

Simply issue the following command:

rvm get stable

RVM will then fetch and install the newest version and then reload the environment. This is where your configurations would be wiped out if you placed them in config/db instead of user/db.

If you would like to upgrade to the latest available version (not necessarily stable), you can type:

rvm get head

Conclusion

As you can see, RVM is a versatile tool that can be used to manage projects and entire Ruby environments. You can use RVM to configure development conditions, server installations, and even to deploy your application.

Include Vs Extend Vs Load Vs Require

Include

When you Include a module into your class as shown below, it’s as if you took the code defined within the module and inserted it within the class, where you ‘include’ it. It allows the ‘mixin’ behavior. It’s used to DRY up your code to avoid duplication, for instance, if there were multiple classes that would need the same code within the module.

The following assumes that the module Log and class TestClass are defined in the
same .rb file. If they were in separate files, then ‘load’ or ‘require’ must be used to let the class know about the module you’ve defined.

module Log
def class_type
"This class is of type: #{self.class}"
end
end

class TestClass
include Log
# ...
end

tc = TestClass.new.class_type
puts tc

Load

The load method is almost like the require method except it doesn’t keep track of whether or not that library has been loaded. So it’s possible to load a library multiple times and also when using the load method you must specify the “.rb” extension of the library file name.

Most of the time, you’ll want to use require instead of load but load is there if you want a library to be loaded each time load is called. For example, if your module changes its state frequently, you may want to use load to pick up those changes within classes loaded from.

Here’s an example of how to use load. Place the load method at the very top of
your “.rb” file. Also the load method takes a path to the file as an argument:

load 'test_library.rb'

So for example, if the module is defined in a separate .rb file than it’s used, then you can use the
File: log.rb

Require

The require method allows you to load a library and prevents it from being loaded more than once. The require method will return ‘false’ if you try to load the same library after the first time. The require method only needs to be used if library you are loading is defined in a separate file, which is usually the case.
So it keeps track of whether that library was already loaded or not. You also don’t need to specify the “.rb” extension of the library file name.
Here’s an example of how to use require. Place the require method at the very top
of your “.rb” file:
require ‘test_library’

Extend

When using the extend method instead of include, you are adding the module’s
methods as class methods instead of as instance methods.

Here is an example of how to use the extend method:

module Log
def class_type
"This class is of type: #{self.class}"
end
end

class TestClass
extend Log
# ...
end

tc = TestClass.class_type
puts tc

The above will print “This class is of type: TestClass”
When using extend instead of include within the class, if you try to instantiate
TestClass and call method class_type on it, as you did in the Include example
above, you’ll get a NoMethodError. So, again, the module’s methods become
available as class methods.

Rails get total count of child: has_many/through associations group_by

Find all Courses count for particular Staff member with staff member name

Example

Model-1 : Staff

Model-2: Course

Association: Staff has_many Courses
Course belongs_to Staff

a = Staff.joins(:courses).select('staff.id, staff.first_name, count(courses.id) as n_courses').group('staff.id')

a.each {|staff| puts "#{staff.first_name}:#{staff.n_courses}"}