© Stefan Wintermeyer 2018
Stefan WintermeyerLearn Rails 5.2https://doi.org/10.1007/978-1-4842-3489-1_6

6. Bundler and Gems

Stefan Wintermeyer1 
(1)
Bochum, Germany
 

Gems are how you do package management in the world of Ruby.

../images/460214_1_En_6_Chapter/460214_1_En_6_Figa_HTML.gif If you do not have much time, you can skip this chapter for now and come back to it later if you have any specific questions.

If a Ruby developer wants to offer a specific feature or a certain program or collection of programs to other Ruby developers, the developer can create a package. Those packages are called gems . They can then be installed with the command gem install.

../images/460214_1_En_6_Chapter/460214_1_En_6_Figb_HTML.gif Take a look at https://www.ruby-toolbox.com to get an overview of the existing gems.

Rails itself is a gem, and every Rails project uses a lot of different gems. You as a developer can even add other gems. The program bundle helps the developer to install all these gems in the right version and to take the dependencies into account.

The file Gemfile generated by rails new indicates which gems are to be installed by Bundler, as shown in Listing 6-1.

source 'https://rubygems.org'
git_source(:github) { |repo| "https://github.com/#{repo}.git" }
ruby '2.5.0'
# Bundle edge Rails instead: gem 'rails', github: 'rails/rails'
gem 'rails', '~> 5.2.0'
# Use sqlite3 as the database for Active Record
gem 'sqlite3'
# Use Puma as the app server
gem 'puma', '~> 3.11'
# Use SCSS for stylesheets
gem 'sass-rails', '~> 5.0'
# Use Uglifier as compressor for JavaScript assets
gem 'uglifier', '>= 1.3.0'
# See https://github.com/rails/execjs#readme for more supported runtimes
# gem 'mini_racer', platforms: :ruby
# Use CoffeeScript for .coffee assets and views
gem 'coffee-rails', '~> 4.2'
# Turbolinks makes navigating your web application faster. Read more: https://github.com/turbolinks/turbolinks
gem 'turbolinks', '~> 5'
# Build JSON APIs with ease. Read more: https://github.com/rails/jbuilder
gem 'jbuilder', '~> 2.5'
# Use Redis adapter to run Action Cable in production
# gem 'redis', '~> 4.0'
# Use ActiveModel has_secure_password
# gem 'bcrypt', '~> 3.1.7'
# Use ActiveStorage variant
# gem 'mini_magick', '~> 4.8'
# Use Capistrano for deployment
# gem 'capistrano-rails', group: :development
# Reduces boot times through caching; required in config/boot.rb
gem 'bootsnap', '>= 1.1.0', require: false
group :development, :test do
  # Call 'byebug' anywhere in the code to stop execution and get a debugger console
  gem 'byebug', platforms: [:mri, :mingw, :x64_mingw]
  # Adds support for Capybara system testing and selenium driver
  gem 'capybara', '~> 2.15'
  gem 'selenium-webdriver'
  # Easy installation and use of chromedriver to run system tests with Chrome
  gem 'chromedriver-helper'
end
group :development do
  # Access an interactive console on exception pages or by calling 'console' anywhere in the code.
  gem 'web-console', '>= 3.3.0'
  gem 'listen', '>= 3.0.5', '< 3.2'
  # Spring speeds up development by keeping your application running in the background. Read more: https://github.com/rails/spring
  gem 'spring'
  gem 'spring-watcher-listen', '~> 2.0.0'
end
# Windows does not include zoneinfo files, so bundle the tzinfo-data gem
gem 'tzinfo-data', platforms: [:mingw, :mswin, :x64_mingw, :jruby]
Listing 6-1

Gemfile

The format used is easy to explain: the word gem is followed by the name of the gem and then, if required, a specification of the version of the gem.

For example, the line gem 'rails', '5.2.0' means “install the gem with the name rails in the version 5.2.0.”

With ~> before the version number, you can determine that the newest version after this version number should be installed. As a result, the last digit is incremented, so for example gem 'rails', '~> 4.0.0' would correspondingly install Rails 4.0.1 but not 4.1 (for the latter, you would need to specify gem 'rails', '~> 4.1').

../images/460214_1_En_6_Chapter/460214_1_En_6_Figc_HTML.gif You have the option of installing certain gems only in certain environments. To do so, you need to enclose the corresponding lines in a group :name do loop.

Besides the file Gemfile, there is also the file Gemfile.lock , and the exact versions of the installed gems are listed there. For the previous example, it looks like Listing 6-2.

GEM
  remote: https://rubygems.org/
  specs:
    actioncable (5.2.0)
      actionpack (= 5.2.0)
      nio4r (~> 2.0)
      websocket-driver (~> 0.6.1)
    actionmailer (5.2.0)
      actionpack (= 5.2.0)
      actionview (= 5.2.0)
      activejob (= 5.2.0)
      mail (~> 2.5, >= 2.5.4)
      rails-dom-testing (~> 2.0)
    actionpack (5.2.0)
      actionview (= 5.2.0)
      activesupport (= 5.2.0)
      rack (~> 2.0)
      rack-test (>= 0.6.3)
      rails-dom-testing (~> 2.0)
      rails-html-sanitizer (~> 1.0, >= 1.0.2)
      [...]
Listing 6-2

Gemfile.lock

The advantage of Gemfile.lock is that it makes it possible for several developers to work on the same Rails project independently from one another and to still be sure that they are all working with the same gem versions. If a file version is locked in Gemfile.lock, this version is used by Bundler. This is also useful for deploying the Rails project later on a web server.

../images/460214_1_En_6_Chapter/460214_1_En_6_Figd_HTML.gif Edit only Gemfile and never Gemfile.lock.

Thanks to this mechanism, you can use and develop several Rails projects with different gem version numbers in parallel.

bundle update

With bundle update , you can update gems to new versions. For example, here is a Rails project with the Rails version 4.2.1:

$ rails -v
Rails 4.2.1
$

In the file Gemfile, this version is listed, as shown in Listing 6-3.

source 'https://rubygems.org'
# Bundle edge Rails instead: gem 'rails', github: 'rails/rails'
gem 'rails', '4.2.1'
[...]
Listing 6-3

Gemfile

It’s also listed in Gemfile.lock.

$ grep 'rails' Gemfile.lock
  [...]
  rails (= 4.2.1)
  [...]
$

Assume you are working with Rails 4.2.0 and you want to update it to Rails 4.2.4. You have to change the Gemfile from what’s shown in Listing 6-4 to what’s shown in Listing 6-5.

[...]
gem 'rails', '4.2.0'
[...]
Listing 6-4

Gemfile

[...]
gem 'rails', '4.2.4'
[...]
Listing 6-5

Gemfile

After this change, you can use bundle update rails to install the new Rails version (Bundler automatically takes the required dependencies into account).

$ bundle update rails
  [...]
$ rails -v
Rails 4.2.4
$

../images/460214_1_En_6_Chapter/460214_1_En_6_Fige_HTML.gif After every gem update, you should first run rake test to make sure that a new gem version does not add any unwanted side effects.

bundle outdated

If you want to know which of the gems used by your Rails project are now available in a new version, you can do this via the command bundle outdated . Here’s an example:

$ bundle outdated
The dependency tzinfo-data (>= 0) will be unused by any of the platforms Bundler is installing for. Bundler is installing for ruby but the dependency is only for x86-mingw32, x86-mswin32, x64-mingw32, java. To add those platforms to the bundle, run `bundle lock --add-platform x86-mingw32 x86-mswin32 x64-mingw32 java`.
Fetching gem metadata from https://rubygems.org/.........
Fetching gem metadata from https://rubygems.org/.
Resolving dependencies....
Outdated gems included in the bundle:
  * archive-zip (newest 0.10.0, installed 0.7.0)
  * websocket-driver (newest 0.7.0, installed 0.6.5)

To update them, you’ll have to change the version numbers in Gemfile and run a bundle update command.

bundle exec

bundle exec is required whenever a program such as rake is used in a Rails project and is present in a different version than the rest of the system. The resulting error message is always easy to implement.

You have already activated rake 0.10, but your Gemfile requires rake 0.9.2.2.
Using bundle exec may solve this.

In this case, it helps to invoke the command with a preceding bundle exec command, as shown here:

$ bundle exec rake db:migrate

binstubs

In some environments, using bundle exec is too complicated. In that case, you can install programs with the correct version via bundle install --binstubs in the directory bin.

$ bundle install --binstubs
Using rake 12.3.0
Using concurrent-ruby 1.0.5
Using i18n 0.9.1
[...]
Using turbolinks 5.1.0
Using uglifier 4.1.3
Using web-console 3.5.1
Bundle complete! 18 Gemfile dependencies, 76 gems now installed.
Use `bundle info [gemname]` to see where a bundled gem is installed.

Afterward, you can always use these programs. Here’s an example:

$ bin/rake db:migrate
==  CreateUsers: migrating ================================================
-- create_table(:users)
   -> 0.0018s
==  CreateUsers: migrated (0.0019s) =======================================

Popular Gems

At https://www.ruby-toolbox.com you’ll find most of the available gems. The main problem with gems is that many times you have no idea how active the community is that developed a gem. It’s a major headache to upgrade a Rails application that uses neglected gems. So, you can check out the gem’s home page and GitHub repository before installing a gem.

I’ll show you a couple of gems that are essential for many developers. But please do your due diligence first before you include a gem!

acts_as_list

Let’s create a to-do list application that displays a couple of to-dos that can be edited by the user. You just need one scaffold for this. Let’s call the model task. Here is the basic setup:

$ rails new to-do-list
  [...]
$ cd to-do-list
$ rails generate scaffold task name completed:boolean
  [...]
$ rails db:migrate
  [...]
$ rails server

../images/460214_1_En_6_Chapter/460214_1_En_6_Figf_HTML.gif Naming is always important within a Rails project. I’ve seen many examples of a to-do list application where the Task model has a field called task. Don’t do that. If you have an instance variable called @task, it is cleaner to have a @task.name than a @task.task, which is just confusing.

Order Your Tasks

A common idea for any to-do list is the feature to order the tasks . For that you’ll need to have some sort of position field in your model. Because this is such a common problem, there is a nice gem ready to go for this. It’s called acts_as_list. To use it, you have to add the line shown in Listing 6-6 to the Gemfile and run the bundler.

[...]
gem 'acts_as_list'
[...]
$ bundle
Listing 6-6

Gemfile

To use it, you have to add a position field to the task model.

$ rails generate migration AddPositionToTask position:integer
  [...]
$ rails db:migrate

If you already have a full database table of tasks, you will want to change the migration to something like this, which sets the position field:

class AddPositionToTask < ActiveRecord::Migration[5.2]
  def change
    add_column :tasks, :position, :integer
    Task.order(:updated_at).each.with_index(1) do |task, index|
      task.update_column :position, index
    end
  end
end

The last change is a change to the task model to make it use acts_as_list, as shown in Listing 6-7.

class Task < ApplicationRecord
  acts_as_list
end
Listing 6-7

app/models/task.rb

For any new entry of the tasks table, acts_as_ list will set the position field automatically. But that is not all. You can use these methods to move the position of a task and reorder the list:
  • task.move_lower

  • task.move_higher

  • task.move_to_bottom

  • task.move_to_top

You also have access to these useful methods:
  • task.first?

  • task.last?

  • task.in_list?

  • task.not_in_list?

  • task.higher_item

  • task.higher_items

  • task.lower_item

  • task.lower_items

It’s not rocket science, but it’s so much easier to use an existing gem than to reinvent the wheel.

Don’t forget to change the index action in your tasks_controller.rb file to display the tasks in the right order, as shown in Listing 6-8.

[...]
def index
  @tasks = Task.order(:position)
end
[...]
Listing 6-8

app/controllers/tasks_controller.rb

Check Done Tasks in Your Index View

Wouldn’t it be nice to have a way of checking done tasks in the /tasks index view instead of having to use the edit view every time? This could be done with a link to a yet to be created check action in app/controllers/tasks_controller.rb. But there is a cleaner, more RESTful way: you can use the update action from a little form in each table row.

Listing 6-9 shows the example code snippet for app/views/tasks/index.html.erb.

[...]
<% @tasks.each do |task| %>
  <tr>
    <td><%= task.description %></td>
    <td><%= task.completed %></td>
    <td>
      <% unless task.completed %>
        <%= form_with(model: task, local: true) do |form| %>
          <%= form.hidden_field :completed, value: true %>
          <div class="actions">
            <%= form.submit 'Check!', :name => 'check' %>
          </div>
        <% end %>
      <% end %>
    </td>
    <td><%= link_to 'Show', task %></td>
    <td><%= link_to 'Edit', edit_task_path(task) %></td>
    <td><%= link_to 'Destroy', task, method: :delete, data: { confirm: 'Are you sure?' } %></td>
  </tr>
<% end %>
[...]
Listing 6-9

app/views/tasks/index.html.erb

Find more information and the complete documentation about acts_as_list at https://github.com/swanandp/acts_as_list .

Authentication

Most Rails applications need some kind of authentication system . The old RailsCast episode at http://railscasts.com/episodes/250-authentication-from-scratch-revised shows how to do that by yourself. It is not that complicated, but it is also nice to do authentication with a ready-to-go gem that not only handles passwords but also sends one-time password e-mails and does the Facebook and Twitter magic. This saves you a lot of time that you can instead invest in your application.

Take a look at https://www.ruby-toolbox.com/categories/rails_authentication , which sorts the most popular authentication gems. I’ve used a couple of them, but I don’t have a clear favorite.

If you have the time, try two to three for yourself. If you don’t have the time, go with devise by Plataformatec ( https://github.com/plataformatec/devise ).

Authorization

Authentication is only half the battle. You need to have a system to limit access to special parts of your Rails application to specific users or user groups. In other words, you need an authorization system . Again, you can create such a system by yourself; it is not rocket science. But if you are in a hurry, go to https://www.ruby-toolbox.com/categories/rails_authorization to find a list of available gems for this.

However, do not use the outdated cancan by the Rails legend Ryan Bates (the inventor of http://railscasts.com ). It is an orphan. Use cancancan, which is an up-to-date fork. You’ll find it at https://github.com/cancancommunity/cancancan .

Simple Form

Many Rails developers use the simple_form gem ( https://github.com/plataformatec/simple_form ) to make their lives easier. It helps you create forms in an easier way than the default scaffolds. Please see for yourself. I found this topic a double-edged sword. I try to stay as vanilla as possible, but I see the attractiveness of simple_form.

Further Information on Bundler

The topic of Bundler is far more complex than can be described here. If you want to find out more about Bundler, please visit the following web sites:
..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
18.218.81.166