Bundler
Bundler is Ruby's dependency manager — it lets you install, update, and manage gems in your project.
bundle init
Let's write a small program that calls the GitHub API and prints the response. We'll use the http and pry libraries.
Create a lecture4 folder and run:
bundle init
This generates a Gemfile:
# frozen_string_literal: true
source "https://rubygems.org"
# gem "rails"
Any gem you want to use must be declared in this file.
bundle install
Add the two gems to your Gemfile:
gem 'pry'
gem 'http'
Then run:
bundle install
This installs the gems and generates a Gemfile.lock with the exact resolved versions.
Now create repo_info.rb:
require 'http'
require 'json'
require 'pry'
class RepoInfo
attr_reader :repo_name
GITHUB_API_URL = 'https://api.github.com'
def initialize(repo_name)
@repo_name = repo_name
end
def call
data = parse_response(fetch_repo)
puts fetch_repo_info(data)
end
private
def fetch_repo
HTTP.get("{GITHUB_API_URL}/repos/{repo_name}").to_s
end
def parse_response(resp)
JSON.parse(resp)
rescue => e
puts "Something was wrong: {e}"
end
def fetch_repo_info(data)
return 'No data' unless data && data['id']
<<~HERE
Name: {data['name']}, Description: {data['description']}"
Homepage: {data['homepage']}
HERE
end
end
RepoInfo.new('rails/rails').call
puts '---'
RepoInfo.new('rails/rail').call
Running it:
$ ruby repo_info.rb
=>
Name: rails, Description: Ruby on Rails"
Homepage: https://rubyonrails.org
---
No data
bundle update
There are a few nuances depending on how you specify gem versions:
gem 'pry' # always updates to the latest version
gem 'pry', '0.14.0' # locks to exactly 0.14.0
gem 'pry', '~> 0.14.0' # allows 0.14.x but not 0.15
Read more: Semantic Versioning
bundle exec
Runs a command using the gem versions specified in your Gemfile, not whatever is installed globally on the system:
bundle exec rails s
bundle exec rake tmp:my_task
More commands: bundler.io/docs.html
Gems
A gem is a Ruby library. You can manage gems directly with the gem command:
gem install pry # install
gem search ^pry # search
gem list # list installed gems
gem uninstall pry # uninstall
That said, it's better to manage gems through Bundler in most projects.
Pry Debugger
Pry lets you pause Ruby execution at any point by inserting binding.pry:
def call
data = parse_response(fetch_repo)
binding.pry
puts fetch_repo_info(data)
end
When execution hits that line, you get an interactive console where you can inspect variables or run any Ruby code:
From: repo_info.rb:16 RepoInfo#call:
14: def call
15: data = parse_response(fetch_repo)
=> 16: binding.pry
17: puts fetch_repo_info(data)
18: end
Type help to see all available commands. Some useful ones:
ls # show vars and methods in current scope
cd # move into a new context
whereami # show surrounding code
wtf? # show backtrace of the last exception
View documentation inline:
ri String#downcase
View source of any library:
show-source -a Pry
Coding Style — RuboCop
RuboCop is a Ruby code analyzer (linter) and formatter. Add it to your Gemfile:
gem 'rubocop', require: false
Run it:
$ rubocop repo_info.rb
Offenses:
repo_info.rb:1:1: C: Style/FrozenStringLiteralComment: Missing frozen string literal comment.
repo_info.rb:5:1: C: Style/Documentation: Missing top-level class documentation comment.
repo_info.rb:8:20: C: Style/MutableConstant: Freeze mutable objects assigned to constants.
repo_info.rb:30:3: C: Style/RescueStandardError: Avoid rescuing without specifying an error class.
1 file inspected, 7 offenses detected, 6 offenses auto-correctable
Auto-fix correctable offenses:
rubocop -a repo_info.rb
You can also create a .rubocop.yml config file to customize rules:
Layout/LineLength:
Max: 120
Regular Expressions
Regular expressions let you match and extract text by pattern. In Ruby:
/[a-zA-Z]/
%r{[a-zA-Z]}
Use match or =~ for matching:
"zvlex@vaba.co".match(/[a-zA-z]+@vaba.co/)
=> #
"zvlex@example.com".match(/[a-zA-z]+@vaba.co/)
=> nil
The MatchData object can be accessed with result[0], result[1], etc.
Extracting named values from a string:
info = "Name: Alex
Age: 28"
data = /Name: (w+)
Age: (d+)/.match(info)
name, age = data[1], data[2]
puts name # => Alex
puts age # => 28
Great tool for practicing regexps: rubular.com
Set
A Set is a hybrid Array+Hash that stores only unique elements:
require 'set'
s1 = Set[1, 2, 3, 4, 5]
s2 = Set[3, 5, 7, 9]
s3 = Set[3, 5, 7, 9]
s2 == s3 # => true
s1.add(6) # => #
s1.add(5) # => # (no duplicate)
s1.merge(s2) # => #
s2.subset?(s3) # => true
Date, Time, and DateTime
Date
require 'date'
d = Date.new(2021, 9, 12)
puts d.year, d.month, d.day
# => 2021, 9, 12
d = Date.parse("2021-09-12")
(Date.parse("2021-09-25") - Date.parse("2021-09-11")).to_i
# => 14
Time
t = Time.now
puts t.hour, t.min, t.sec
timestamp = Time.now.to_i
# => 1631458963
Time.at(timestamp)
# => 2021-09-12 19:02:43 +0400
DateTime
Note: DateTime does not account for leap seconds or DST.
require 'date'
d = DateTime.parse("2021-09-12T19:06:33")
puts d.year, d.month, d.day, d.hour, d.min, d.sec
# => 2021, 9, 12, 19, 6, 33
Use strftime to format date/time output:
Time.now.strftime("%Y-%m-%d %H:%M:%S")