codedecoder

breaking into the unknown…


3 Comments

undefined method `visit’ for rspec capybara

Recently, I am trying to integrate rspec and capybara with sinatra (see here). But when I try to run my test file, I get below error

NoMethodError:
undefined method `visit’ for #<RSpec::Core::ExampleGroup::Nested_1:0x9209d5c @example=nil>

I find it strange, as I have configured capybara many a time with rails and it work smoothly. I get some hint of problem here on stack overflow.

visit is a method defined by capybara over rspec layer i,e it is DSL (domain specific language). when working with rails, capybara DSL module is automatically loaded by rails when rspec is loaded, but with sinatra, this is not happening. So we have to include  Capybara::DSL in the file we want to use its method. I have added the include line in my spec_helper.rb file which is required in my all other test files.

SOLUTION : add include Capybara::DSL to your configuration file or the individual test file

My, spec_helper.rb file look like this

require 'capybara'
include Capybara::DSL # Adding this line solved the error
Capybara.default_driver = :selenium

def app 
  Sinatra::Application 
end 

set :environment, :test 
RSpec.configure do |config| 
  config.treat_symbols_as_metadata_keys_with_true_values = true 
  config.run_all_when_everything_filtered = true 
  config.filter_run :focus config.order = 'random' 
end


2 Comments

sample app with sinatra rspec capybara and selenium

I have been working with rails rspec capybara and selenium for long, but recently I have to build an API for client which he specifically requested in sinatra. I will list difference between rails and sinatra some other days. For now, I can quote below characteristic for sinatra

1->It not a framework i,e no folder , configuration file etc get created
2->no mvc concept, whole project may be written in a single file
3->can pack your app as a gem which can be used by other . ex : resque for managing background job is build on sinatra

You can download a no of available ebook on sinatra to know it in detail. Here, I will just create a “Hello sinatra” app and configure it with rspec capybara and selenium. STEPS 1 to 6 is just the basic steps Iam following in all the rails project as so adopted it here also a good practice

STEP 1: create your project folder and call it say sinatra_app

STEP 2: create a gemset sinatra_app if you are using RVM(I suggest all ruby dveloper to use it ) # detail on RVM is avilable here and here

If you are not using RVM, install bundler gem first with the command sudo gem install bundler. more detail on bundler is available here

STEP 3: create a file with name Gemfile in this folder and add the required gems to it. The Gemfile content will look like this

source 'https://rubygems.org'

gem "thin" # sinatra by default run with thin, so this gem is also required
gem "sinatra"

group :test do
  gem "rspec", ">= 1.2.1"
  gem "capybara", ">= 1.1.2"
end

STEP 4: If you are using RVM create a .rvmrc file in your sinatra_app folder and add below line to it.

It will load the proper ruby version and your sinatra_app gemset everytime you cd to the sinatra_app folder

rvm ruby-1.9.3-p194@sinatra_app # Iam assuming that you are using ruby version rvm ruby-1.9.3-p194 and your gemset name is sinatra_app, change it accordingly

STEP 5 : go to terminal and cd to sinatra_app folder and run below command

$ bundle install # you will slow below trace on the terminal
Fetching gem metadata from https://rubygems.org/.........
Fetching gem metadata from https://rubygems.org/..
Using addressable (2.3.2) 
Using mime-types (1.19) 
Installing nokogiri (1.5.6) with native extensions 
Installing rack (1.4.3) 
Using rack-test (0.6.2) 
Installing ffi (1.3.0) with native extensions 
Installing childprocess (0.3.6) 
Installing websocket (1.0.6) 
Installing libwebsocket (0.1.7.1) 
Installing multi_json (1.5.0) 
Using rubyzip (0.9.9) 
Installing selenium-webdriver (2.27.2) 
Installing xpath (1.0.0) 
Installing capybara (2.0.2) 
Using daemons (1.1.9) 
Using diff-lcs (1.1.3) 
Using eventmachine (1.0.0) 
Using rack-protection (1.3.2) 
Installing rspec-core (2.12.2) 
Installing rspec-expectations (2.12.1) 
Installing rspec-mocks (2.12.1) 
Installing rspec (2.12.0) 
Using tilt (1.3.3) 
Using sinatra (1.3.3) 
Installing thin (1.5.0) with native extensions 
Using bundler (1.1.5)

Thus you can see that all our required gem thin, rspec,and  capybara get installed along with there dependency. You can also see a new file Gemfile.lock get created in sinatra_app folder. Since, our base is now ready, so better push our code to git in the next step

STEP 6 : pushing the existing files to GIT

$ cd sinatra_app #all mentioned command here will run only after moving to sinatra_app folder
$ git init .
$ git add .
$ git commit -a -m"my initial commit"

STEP 7: create my_sinatra_app.rb file and below line to it

require 'sinatra'
get '/' do
  "Hello....Sinatra :)"
end

STEP 8: Run the app on terminal with ruby command

$ ruby loanpath_api.rb
== Sinatra/1.3.3 has taken the stage on 4567 for development with backup from Thin
>> Thin web server (v1.3.1 codename Triple Espresso)
>> Maximum connections set to 1024
>> Listening on 0.0.0.0:4567, CTRL+C to stop

You can see that thin is Listining to port 4567 at 0.0.0.0

STEP 9: see your app in browser by typing 0.0.0.0:4567 #you can see the loaded page with message “Hello….Sinatra :)”

STEP 10: Adding rspec to sinatra

$ rspec --init
The --configure option no longer needs any arguments, so true was ignored.
  create   spec/spec_helper.rb
  create   .rspec

You can see that it has created two files for you within spec folder. The important one is spec_helper.rb. The default content of this file is as below

RSpec.configure do |config|
  config.treat_symbols_as_metadata_keys_with_true_values = true
  config.run_all_when_everything_filtered = true
  config.filter_run :focus
  config.order = 'random'
end

we will add, the require configuration to make rspec work with sinatra in the next step. Infact, any rspec configuration should go to this file.

STEP 11 : spec_helper.rb content after adding setting for sinatra is as below

require 'rack/test' # it is needed to run rspec
include Rack::Test::Methods # It contain different method like get,  last_response etc which
 you use to write your test

def app
  Sinatra::Application # It is must and tell rspec that test it running is for sinatra
end

set :environment, :test # setting the environment in which the test will run

RSpec.configure do |config|
  config.treat_symbols_as_metadata_keys_with_true_values = true
  config.run_all_when_everything_filtered = true
  config.filter_run :focus
  config.order = 'random'
end

STEP 12 : Writing the first test case
create a file with any name, but as a good practice, it should be same as the file you are testing so we will call it my_sinatra_app_specs.rb and add below line to it

require_relative '../my_sinatra_app.rb' # this load the file you are testing
require 'spec_helper.rb' # It will load the configuration you set in spec_helper.rb

describe 'sinatra app home page' do	
	it "should load the home page" do # the first test
	  get '/' # you are visiting the home page
	  last_response.should be_ok # it will true if the home page load successfully
	end
end

STEP 13 : Run test file my_sinatra_app_specs.rb with rspec

$ rspec spec/my_sinatra_app_specs.rb
Run options: include {:focus=>true}

All examples were filtered out; ignoring {:focus=>true}
.

Finished in 0.01908 seconds
1 example, 0 failures

Randomized with seed 28600

Thus, you can see that, our test has pass. till now we are using methods available to rspec, if you want to use methods like visit, response etc it will throw error as they are methods of capybara which we have still not configured. we will configure capybara and selenium in next step and then modify our test to use visit and response instead of get and last_reponse

STEP 14 : configuring rspec to work with capybara and selenium

Since, now we are going to use capybara, we do not need get, last_rsponse etc method of  Rack::Test::Methods, so we will remove the top two line and replace them with capybara configuration. The spec_helper.rb will now look as below. You can read more about capybara here

require 'capybara' # loading capybara
include Capybara::DSL # It contain all the methods you use for writing test. If you do not use it , 
get this error.
Capybara.default_driver = :selenium # it will tell capybara to use selenium. It should be noted that  
By default, Capybara uses the :rack_test driver, which is fast but limited: it does not support 
JavaScript, nor is it able to access HTTP resources outside of your Rack application, such as remote 
APIs and OAuth services.

def app 
  Sinatra::Application 
end 

set :environment, :test 
RSpec.configure do |config| 
  config.treat_symbols_as_metadata_keys_with_true_values = true 
  config.run_all_when_everything_filtered = true 
  config.filter_run :focus config.order = 'random' 
end

STEP 15 : modifying our test case

We will now rewrite, our test case in steps 12 with capybara methods like visit ,page etc

describe 'make API call to loanpath' do	
  it "should load the home page" do
    visit 'http://0.0.0.0:4567'
    page.should have_content("sinatra")
  end
end

Now, when you run your test again with rspec spec/my_sinatra_app_specs.rb command, you can see capybara working and selenium web driver simulating your app


1 Comment

devise login with authentication_token

Devise is a great gem to manage user authentication. You can also authenticate a user i,e make user to login with authentication token. By default token_authentication is inactive for devise but you can activate it with few changes. Devise provide different support for token authentication like creating tokens, validating with passed token, expiring the token etc. The detail list of methods is documented here. I will just list here the changes which will make you existing devise authentication to support token based authentication. I am assuming that the model you are authenticating with devise is User. I will underline any modification we made to existing code

STEP 1:  Add authentication_token field to user model

If you see Your user migration, you will find different devise introduced fields. many of the field which is not needed by default is commented out. Below is the default migration generated by devise for user

class DeviseCreateUsers < ActiveRecord::Migration   def change     create_table(:users) do |t|       ## Database authenticatable       t.string :email,              :null => false, :default => ""
      t.string :encrypted_password, :null => false, :default => ""

      ## Recoverable
      t.string   :reset_password_token
      t.datetime :reset_password_sent_at

      ## Rememberable
      t.datetime :remember_created_at

      ## Trackable
      t.integer  :sign_in_count, :default => 0
      t.datetime :current_sign_in_at
      t.datetime :last_sign_in_at
      t.string   :current_sign_in_ip
      t.string   :last_sign_in_ip

      ## Confirmable
      # t.string   :confirmation_token
      # t.datetime :confirmed_at
      # t.datetime :confirmation_sent_at
      # t.string   :unconfirmed_email # Only if using reconfirmable

      ## Lockable
      # t.integer  :failed_attempts, :default => 0 # Only if lock strategy is :failed_attempts
      # t.string   :unlock_token # Only if unlock strategy is :email or :both
      # t.datetime :locked_at

      ## Token authenticatable
      # t.string :authentication_token

    end

    add_index :users, :email,                :unique => true
    add_index :users, :reset_password_token, :unique => true
    # add_index :users, :confirmation_token,   :unique => true
    # add_index :users, :unlock_token,         :unique => true
    # add_index :users, :authentication_token, :unique => true
  end
end

So you can see that field needed for Confirmable,  Lockable and  Token authenticatable module of devise is commented out as they are not the default. whenever you want to activate any module you need to add those fields to the users table. You can see that for authenticable module we need one field and then add index to that. So we will define a new migration and add the required field

class AddAuthenticationTokenToUser < ActiveRecord::Migration   
  def change     
    add_column :users, :authentication_token, :string     
    add_index  :users, :authentication_token, :unique => true
  end
end

run the migration to introduce the newly added filed rake db:migrate

STEP 2: Modifying the routes.rb for devise

Since, authentication_token concept is basically use to authenticate user from outside i,e say you sent user a email containing the activation link containing the token, so when he click the link he directly get loged in. Similarly, If a User want to access your site from some other site he can access it by authenticating himself by passing proper token in signin url. We need to tell Devise what will be the name of the key which will hold the authentication_key let us call it authentication_key itself

devise_for :users, :token_authentication_key => 'authentication_key'

STEP 3: Modifying the model

In model we will do below modification

class User < ActiveRecord::Base

  devise :database_authenticatable, :registerable, :recoverable, :rememberable, :trackable,
    :validatable, :timeoutable, :omniauthable , :token_authenticatable # the underline is 
    added to existing code, it will activate authenticable module of Devise

  before_save :ensure_authentication_token # whenever a user is saved i,e created or updated it
  will see that a unique authentication token get created if not already exist

end

STEP 4: Modifying the Application controller

Now we are ready to go, Devise will take care of everything. It provide a empty method after_token_authentication as hook in which we will do our stuff if the user is authenticated. So we will set a before_filter on this method.

class ApplicationController < ActionController::Base

  before_filter :after_token_authentication # it is empty hook provided by devise i,e 
  once user is successfully authenticated with the token devise look for this method , 
  and execute the code there

  def after_token_authentication
    if params[:authentication_key].present?
      @user = User.find_by_authentication_token(params[:authentication_key]) # we are finding 
      the user with the authentication_key with which devise has authenticated the user
      sign_in @user if @user # we are siging in user if it exist. sign_in is devise method 
                               to sigin in any user
      redirect_to root_path # now we are redirecting the user to root_path i,e our home page
    end
  end

end

STEP 5: See it working.

Let ,My application is my_app.com, one of the user have the  authentication token “xyz1234rst” , when he will type below url in browser, he will get loged in and redirected to root_path

http://my_app.com/users/sign_in? authentication_key=xyz1234rst

The user is at the home page of the Site…So great….Its working 🙂


2 Comments

exception handling in ruby rails

If there is any problem in your code at run time , say your code at some point of time try to divide a number with 0, ruby will throw exception and so breaking your code and bringing down your application. As a developer, you should visualize the possibility of such exception and handle them gracefully within begin/end block.You can read more about ruby exception class here at ruby-doc.org.The different exception thrown by ruby is as below in hierarchical order.

NOTE : creating and raising custom exception is explained in this post

RUBY EXCEPTION HIERARCHY

 
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

wherever you see possibility of occurrence of any of these exception in your code, you should wrap that part of code in begin end block and try to rescue it gracefully. Descendants of class Exception are used to communicate between raise methods and rescue statements in begin/end blocks. Exception objects carry information about the exception—its type (the exception’s class name), an optional descriptive string, and optional traceback information. We can handle an exception in following ways.

NOTE : ruby use raise method internally to trigger any of the above exception. You may use raise anywhere in your code to trigger your own exception explicitly.

Exception handling examples :

I’ am just specifying the syntax of begin rescue ensure and end blocks.

Example 1:

Here, you are aware, what type of above error your code may throw. this format is useful only if you know beforehand what exception your code may throw

begin
***** your code which you think may raise Say  ArgumentError ****
rescue  ArgumentError
****** write your handling code for ArgumentError******
end

Example 2:

Here, we will handle one exception in one way, some other in other way and so on i,e you can use rescue any no of time you want in the begin/end block

begin
***** your code which you think may raise Say  ArgumentError and ZeroDivisionError which you want to 
handle differently while in some common ways for all other****
rescue  ArgumentError
****** write your handling code for ArgumentError******
rescue  ZeroDivisionError
****** write your handling code for ZeroDivisionError******
else
****** write your handling code for any other type of exception******
end

Example 3:

Here we will handle any of the above exception thrown by our code. This approach is not supported by many as it will rescue your code from all the exception and you will never know what going wrong and where is the fault point. The general view is to let exception to explode loud and then fix them, in the above two examples it is more practical as you have visualized the possible exception your code may throw and so you have provided the safety net. more discussion on this is available here

begin
***** your code which you think may raise any exception****
rescue  Exception
****** write your handling code ******
end

Example 4:

Exception object also return information about the line in begin block where exception occur with its backtrace method and what is the nature or type of exception in message method. You can use them to debug your code.

NOTE : Use backtrace and message only for debugging. If you commit the code with these two method whenever an exception occur production log may have all the information about database details, application stack etc depending on type of exception thrown. better to remove these line once you have done debugging of your code

begin
***** your code which you think may raise any exception****
rescue  Exception => e
  puts e.backtrace # will print the line where the error occur 
  puts e.message # will print type of the exception and other detail related to it
****** write your handling code ******
end

Example 5:

Here, we will use ensure to do some clean up work after we have rescued the exception. say you code allow a user to open and edit a file, you wrap your code in begin rescue end block to handle any exception, but then here, still you want that despite of any exception, that file may be closed, so ensure keyword will be used

begin
  my_file = File.open("xyz.rb")
  ***** your code which you think may raise any exception****
rescue  Exception
  ****** write your handling code ******
ensure
  my_file.close #after rescuing the exception, the control will jump to the ensure block to 
              do our cleanup stuff or any other thing we want to happen despite a exception
end

RAISING EXCEPTION IN RUBY

You can raise any exception explicitly by using raise method. The raise method is from the Kernel module. By default, raise creates an exception of the RuntimeError class. To raise an exception of a specific class, you can pass in the class name as an argument to raise.

Example 1:

def my_method 
  ***I am doing something**** 
  raise "I am raising this exception" #It will throw exception of default type RuntimeError, 
                                       you can pass specific type as argument 
  ***** doing something else******* 
end

Example 2:

def my_method 
  ***I am doing something**** 
  raise  ZeroDivisionError, "I am raising this exception" # It will throw exception 
                                                                 of type ZeroDivisionError 
  ***** doing something else******* 
end

You can use raise to handle some specific case for any method which is called by many other methods. let say in user model there is a my_files method and you do not want blacklisted users to access the files , so you can raise a exception in this method which may be handle by different methods calling them.

In model

class User < ActiveRecordBase
  def my_files
    **** some code******
    raise ZerodivisionError, " you are not authorized" if self.blacklisted == true 
     #this exception will be handled by you in different methods calling this method by putting 
     it in begin end block
    **** other code *******
    end
 end

In controller

class UserController <  ApplicationController
  def user_activities
    ******some code ******
    begin
      ******some more code ******
      current_user.my_files# it will throw exception if current_user is blacklisted
    rescue ZeroDivisionError # you are rescuing the error
      redirect_to home_path # you are redirecting user to home page..you may handle it 
                              any way you want
    end
  ******some more code ******
 end
end

Custom Exception in Ruby

If the types of exception mentioned above in the hierarchical order do not suit your needs you can define your own exception and use it exactly same as the existing exceptions.let us create a file my_custom_exception.rb in lib folder and add below line to it

class BlacklistedUserError < StandardError
end

So, now we can raise BlacklistedUserError in above case instead of ZeroDivisonError which make our
code more readable.

For more detail on creating, raising and handling custom exception see this post.


Leave a comment

gemfile in rails

Gemfile contain all the gems needed for your projects. You can track the available gems and new upcoming gems in rails community at rubygem.org and rubytoolbox.

when, you create a new Rails application it automatically create a Gemfile for you. For other appliaction, like Sinatra you need to run below command on your project

$ bundle init # It will create Gemfile and Gemfile.lock for you

NOTE : before you install any other gem you should must install bundler gem first. If you are using RVM, it is already installed for you in the global gemset i,e you do not need to do anything

There is some syntax for specifying the gems in the gemfile. The complete detail is available here. Iam listing the important one. So my sample Gemfile look like this.

source “http//rubygems.org” # this is the top line. It specify to bundler that gem in this file is to be installed from which repository. You can add multiple repository also , provided it is a valid gem repository. Some of the other repository are  “http//gems.rubyforge.com”, “http//gems.github.com”, infact you can add your personal repository also

source  “http//gems.github.com”# one more source added. avoid adding multiple source unless you are sure that a particular gem is not hosted on “http//rubygems.org” but on that specific repository because if you use multiple source the bundle install command will take more time to execute as the bundler will try to fetch detail from all the mentioned repository before moving to next line. The good thing is that  “http//rubygems.org”contain most of the gem and is enough.
gem "nokogiri" # it is the simplest form of adding a gem to gem file. You can pass various options like version,
require, git, path and group

gem “oauth”, “>= 1.4.2” # it specify that version should be greater or equal to 1.4.2
gem “RedCloth”, “>= 4.1.0”, “< 4.2.0” # version should be between 4.1.0 and 4.2.0
gem "sqlite3-ruby", :require => "sqlite3" # specify that sqlite3 is the main file, which should be used when autorequiring (Bundler.require). the default is the gem name itself i,e if you do not have passed the required function the main file will be sqlite3-ruby

gem “cancan”, :require => false # it prevent bundler from requiring the gem, but still install it and maintain dependencies.

gem ‘jqgrid_for_rails’, :git => "http://github.com/Juanmcuello/jqgrid_for_rails.git" # it specify that the gem should be installed from its git source rather then rubygem.org repository. I find it useful as for jqgrid some new changes are available on git repository of the gem but has not updated on rubygem.org. while using git , you can also pass
other options branch, tag,ref, and submodules You MUST only specify at most one of these options. The default is :branch => “master”. Specify :submodules => true to cause bundler to expand any submodules included in the git repository

gem "my-admin", :path => "vendor/my-admin" # it will install the gem from the specified path. It would will be helpful for your own gem or any gem you have customised for yourself. Unlike :git, bundler does not compile C extensions for gems specified as paths.
gem "weakling", :platforms => :jruby # It specify that this gem should be use only in jruby platforms. There is a no of other platform also available like mri, :mri_18, rbx, mswin, mingw etc gem "ruby-debug", :platforms => [:mri_18, :jruby]

gem “rspec”, :group => :test # will be loaded only in test environment
gem “wirble”, :groups => [:development, :test] # will be loaded in development and test environment

NOTE :

1->The :git, :path, :group, and :platforms options may be applied to a 
group of gems by using block form.

git "git://github.com/rails/rails.git" do
  gem "activesupport"
  gem "actionpack"
end

platforms :ruby do
  gem "ruby-debug"
  gem "sqlite3-ruby"
end

group :development do
  gem "wirble"
  gem "faker"
end

group :development, :test do
  gem "rspec-rails", ">= 2.11.0"
  gem "factory_girl_rails", ">= 3.5.0"
  gem "debugger"
end

2->placing the gems in group do not prohibit there installation i,e irrespective 
of group in which they are placed all gem and its dependency get installed with bundle 
install command. If you want to skip gem in a particular group use bundler command 
line interface (CLI) --without option

bundle install --without test 
bundle install --without development test 

Similarly, bundler provides two run time methods : Bundler.setup and Bundler.require, 
to limit their impact to particular groups.

# setup adds gems to Ruby's load path
Bundler.setup                    # defaults to all groups
require "bundler/setup"          # same as Bundler.setup
Bundler.setup(:default)          # only set up the _default_ group
Bundler.setup(:test)             # only set up the _test_ group (but `not` _default_)
Bundler.setup(:default, :test)   # set up the _default_ and _test_ groups, but no others

#require requires all of the gems in the specified groups
Bundler.require                  # defaults to just the _default_ group
Bundler.require(:default)        # identical
Bundler.require(:default, :test) # requires the _default_ and _test_ groups
Bundler.require(:test)           # requires just the _test_group

3 -> while deploying in production, better run bundle install - -deployment instead of 
     bundle install. see the detail here


2 Comments

difference between gemfile and gemfile.lock

gemfile and gemfile.lock is at centerstage for managing gem dependency. They are byproduct of bundler gem. If you are using RVM this gem is installed by default. If you are not using rvm install bundler gem first. If you are using rails 3 , it already have code in place to work with bundler, so you need not have to do anything, for Rails before 3 you need to introduce few configuration line which Iam not covering here. The detail use of bundler and its purpose is available here. The syntax for Gemfile is available here.

DIFFERENCE BETWEEN GEMFILE and GEMFILE.LOCK

1-> You write your gem needed for your project in Gemfile i,e Gemfile contain your project gem dependency. But each of these gems may have dependency on one or more other gems, So Gemfile.lock contain complete snapshot of all the gems in Gemfile along with there associated dependency.

For example:

say you add a new gem oauth to your gemfile

gem "oauth", "~> 0.4.7"

But, when you run bundle install, it will install all dependency of this gem and update Gemfile.lock 
accordingly with below additional gems

    actionpack >= 2.3.5
    curb >= 0.6.6.0
    em-http-request >= 0.2.10
    jeweler >= 0
    mocha >= 0.9.8
    rack >= 1.0.0
    rake >= 0
    typhoeus >= 0.1.13

NOTE : rubygems.org is the official ruby gem host and list all gems with there associated dependency. So If you
want to know a gem dependency you can find it there.

2-> You can modify Gemfile content i,e add or delete or edit any gem anytime. But You never touch a Gemfile.lock. It should be modified automatically, when you run bundle install or bundle update command.

3-> Gemfile is used to load all the required gems when rails environment is created say when you start your server or run rake command, But Gemfile.lock is used to maintain consistency in version of gems used by different developers i,e it is loaded when someone run bundle install. Thus, When your co-developers (or you on another machine) check out your code, it will come with the exact versions of all the third-party code your application used on the machine that you last developed on (in the Gemfile.lock). When they run bundle install, bundler will find the Gemfile.lock and skip the dependency resolution step. Instead, it will install all of the same gems that you used on the original machine.

PRECAUTION:

whenever, you made any change to Gemfile and commit it, not forget to commit Gemfile.lock alongwith it, otherwise it will sometime create unwanted headache to other codeveloper. I am mentioning one of my experience as example below.

-> I started a internal demo project with the default sqlit3 gem as database . On running bundle install I got below error

Gem::Installer::ExtensionBuildError: ERROR: Failed to build gem native extension.
/usr/local/bin/ruby extconf.rb
checking for sqlite3.h… no
sqlite3.h is missing. Try ‘port install sqlite3 +universal’
or ‘yum install sqlite-devel’ and check your shared library search path (the
location where your sqlite3 shared library is located).
*** extconf.rb failed ***

I sorted out the issue and committed the Gemfile and Gemfile.lock. see here for more detail on gem native extension error

-> Since, it is internal project, it get handed over to two trainee. on running bundle install, one of them reported the same error with sqlite3 , I suggested him to use mysql2 instead. He added mysql2 to gem file and simply deleted sqlite3 from it  and run bundle install. It basically removed the sqlit3 gem causing the problem from Gemfile.lock. But now he committed only the Gemfile and left out Gemfile.lock .

-> when second trainee pull the code and try to run bundle install, it started failing with sqlite3 error. But to his surprise he do not find any sqlite3 gem in Gemfile.The problem is that the Gemfile.lock still having the sqlite3 in it and since bundler use Gemfile.lock to install the dependency, it is throwing the error. when the first trainee committed the Gemfile.lock which is then  pulled by second trainee the problem get solved.