Gems constitute the package management in the world of Ruby.
Tip
|
If you do not have much time, you can skip this chapter for now and get 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, he can
create a package. Those packages are called "gem". They can then be installed
with the command gem install
.
Tip
|
Have a look at https://www.ruby-toolbox.com to get an overview of the existing gems. |
Rails itself is a gem and any Rails project uses a lot of different gems.
You as a developer can add further gems. The programm bundle
helps the
developer to install all these gems in the right version and to take
into account dependencies.
The file Gemfile
generated by rails new
indicates which gems are
to be installed by Bundler:
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]
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'
stands
for "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 a Rails 4.0.1, but not a 4.1 (for the
latter, you would need to specify gem 'rails', '~> 4.1'
).
Note
|
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.
|
Apart from the file Gemfile
there is also the file Gemfile.lock
and
the exact versions of the installed gems are listed there.
In the above example, it looks like this:
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)
[...]
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 the Gemfile.lock
, this version be used by the Bundler.
This is also useful for deploying the Rails project later on a web server.
Important
|
Only edit Gemfile and never Gemfile.lock .
|
Thanks to this mechanism you can use and develop several Rails projects with different gem version numbers in parallel.
With bundle update
you can update gems to new versions. As an example,
I have a Rails project with the Rails version 4.2.1:
$ rails -v
Rails 4.2.1
$
In the file Gemfile
, this version is listed:
source 'https://rubygems.org'
# Bundle edge Rails instead: gem 'rails', github: 'rails/rails'
gem 'rails', '4.2.1'
[...]
And also in the Gemfile.lock
:
$ grep 'rails' Gemfile.lock
[...]
rails (= 4.2.1)
[...]
$
Assumed we are working with rails 4.2.0 and we want to update to rails
4.2.4. Then we have to change the Gemfile
from this:
[...]
gem 'rails', '4.2.0'
[...]
to this:
[...]
gem 'rails', '4.2.4'
[...]
After this change, you can use bundle update rails
to install the new
Rails version (required dependencies are automatically taken into
account by Bundler):
$ bundle update rails
[...]
$ rails -v
Rails 4.2.4
$
Important
|
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.
|
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
. 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
.
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
:
$ bundle exec rake db:migrate
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.
Afterwards, you can always use these programs. Example:
$ bin/rake db:migrate
== CreateUsers: migrating ====================================================
-- create_table(:users)
-> 0.0018s
== CreateUsers: migrated (0.0019s) ===========================================
On 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. So you want to checkout their homepage and Github Repository first. It’s a major headache to upgrade a Rails application which uses negleted Gems.
I’d like to show you a couple of gems which are essential for many developers. But please do your due diligence first before you include a gem!
Let’s create a To-Do-List application which displays a couple of to-dos which
can be edited by the user. We just need one scaffold for this. We 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
Note
|
Naming is always important within a Rails project. I’ve seen many examples
of the to-do-list application where the Task model has a field task too.
Don’t do that. If you have a instance variable @task it is cleaner to have a
@task.name than a @task.task which is just confusing.
|
A common idea for any to-do-list is the feature to order the tasks. For
that you’ll have to have some sort of position
field in your model.
Because this is such a common problem there is a very nice ready to go
gem for this. It’s called acts_as_list
. To use it we have to add
this line to the Gemfile
and run the bundler:
[...]
gem 'acts_as_list'
[...]
$ bundle
To use it we 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 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 now is a change to the task model to make it use acts_as_list
:
class Task < ApplicationRecord
acts_as_list
end
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
And you have access to these very 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
All not rocket science but so much easier to use the gem than to reinvent a wheel.
And don’t forget to change the index
action in your tasks_controller.rb
to display the tasks in the right order:
[...]
def index
@tasks = Task.order(:position)
end
[...]
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 the
app/controllers/tasks_controller.rb
. But there is a cleaner more RESTful way:
Use the update
action from a little form in each table row.
Here’s 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 %>
[...]
Find more information and the complete documentation about acts_as_list
at
https://github.com/swanandp/acts_as_list
Most Rails applications need some kind of authentication system. The old RailsCast episode http://railscasts.com/episodes/250-authentication-from-scratch-revised shows how to do that by youself and it is not that complicated but it is also nice to do authentication with a good to go gem which not only handles passwords but also sends one time password emails and does the Facebook and Twitter magic. Saves a lot of time you can invest in your application.
Have 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 2-3 for your self. If you don’t have the time go with devise by Plataformatec. https://github.com/plataformatec/devise
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. You need an authorization system. Again, you can create such a system by youself 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.
But do not take 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
Many Rails developers use the simple_form
gem
(https://github.com/plataformatec/simple_form) to make their life easier. It
helps to create forms in an easier way than the default scaffolds. Please see
for your self. I found this topic a double-edged sword. I try to stay as
vanilla as possible but I see the attractiveness of simple_form
.
The topic Bundler is far more complex than can be described here. If you want to find out more on Bundler, please visit the following websites to find further information: