Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

how does rails implicitly import functions from gems?

I am new to ruby and rails. I see that any gems that I have included, I can directly use its functions in my code. I do not see any imports for the code, nor any name spaces for the functions. Isn't this recipe for conflicts, if the gems contain functions with same names ?

So -

How does ruby/rails imports the functions, how does it map a function to a gem ? Can it lead to conflicts when 2 gems contain the same funcitons ? If I want to explicitly use a ruby library, how will I import its code ? Is there namespacing in ruby?

like image 728
murtaza52 Avatar asked Aug 01 '12 15:08

murtaza52


1 Answers

Ruby

The way ruby imports functions declared in one file into another file is via the require function. load accomplishes something similar, but for general purposes, require is usually what you want (see http://ionrails.com/2009/09/19/ruby_require-vs-load-vs-include-vs-extend/ for more details)

hello_lib.rb

def say_hello
  puts "hello"
end

# that's right, you can execute code when a library is required,
# so the sky's the limit of what you can do
puts "Hey, I've been required!"

hello_caller.rb

# load the code from `hello_lib.rb` in the same directory
require './hello_lib.rb'

subdir/hello_other_caller.rb

# for illustrative purposes, adding the parent directory to the load path
# so that ruby will look there for files I want to require
$: << '..'
require 'hello_lib.rb'
say_hello    

Gems

Gems can be thought of as a package of ruby code, or a library. There are a couple ways of loading a gem, but the most common is via require Say you've installed the progressbar gem for displaying simple progress bars in the terminal (gem install progressbar)

progressbar_test.rb

require 'rubygems'
require 'progressbar'

# this also works
# gem 'progressbar', '~> 0.9.2'

pbar = ProgressBar.new("test", 100)
100.times do
  sleep 0.1
  pbar.inc
end
pbar.finish

The reason this works, is that when we require rubygems, it's adding the progressbar gem to the path of where ruby looks for required files.

Rails

Rails is just a collection of gems, some of which provide executable scripts. In previous versions, you had to specify the gems to load, much in the way we did above. But now, with bundler, we can specify all our gems in one Gemfile, along with versioning and source information. Bundler then will work out dependencies amongst the gems and keep our specific versioning for a project in Gemfile.lock. Since bundler itself is a gem, you'll often see code like this:

config/application.rb

require 'bundler'
Bundler.require(:default, Rails.env)

This code tells Bundler to load all of the dependencies we listed flatly in our gemfile, as well as the ones we listed in the group corresponding to the current rails env (e.g. :development).

Namespaces

Yes, you can run into issues in a couple ways. Two gems could have the same name, although they can't be pushed to rubygems in that case, and you'll discover that quickly. A more subtle namespace issue is if two files do something like this:

hello1.rb

def hello
  puts "Hi"
end

hello2.rb

def hello
  puts "Hello, there!"
end

hello3.rb

require './hello1'
require './hello2'
hello

Here we see what happens when we have a namespace conflict in the global namespace. Similar things can happen if two libraries define the same method in the same class name (and this type of monkey patching, while discouraged, still happens!). In practice, you don't run into this too much, especially when good discipline is used in terms of using Modules to namespace code, like if I'm writing a gem hello:

module Hello
  # not such a good name, but won't conflict with ::Object
  class Object
    def to_s
      puts 'this is a bad idea'
    end
  end
end
like image 155
Ben Taitelbaum Avatar answered Sep 21 '22 19:09

Ben Taitelbaum