Ruby 2.5
- Released at: Dec 25, 2017 (NEWS file)
- Status (as of Feb 07, 2023): EOL, latest is 2.5.9
- This document first published: Jun 6, 2019
- Last change to this document: Feb 07, 2023
Highlights
rescue
/ensure
inside blocks#yield_self
Struct
with keyword initialization- Exceptions output changed
Language
Top-level constant look-up is removed.
Foo::Bar
doesn’t fallback to ::Bar
anymore, when Foo::Bar
is not found
- Reason: The behavior was considered to be bringing more problems and bugs than benefits for a long time, and always issued a warning.
- Discussion: Feature #11547
- Code:
Hash::String # Ruby 2.4: # (irb):1: warning: toplevel constant String referenced by Hash::String # => String # Ruby 2.5: # NameError (uninitialized constant Hash::String)
rescue
/else
/ensure
are allowed inside blocks
- Reason: for consistency with other
something
/end
constructs (class and method bodies), which allowedrescue
without wrapping in additionalbegin
/end
- Discussion: Feature #12906
- Code:
(0..5).map do |val| puts 100 / val rescue ZeroDivisionError puts '<undividable>' end # prints: # <undividable> # 100 # 50 # 33 # 25 # 20
- Notice: The syntax is not available for
{}
blocks, even multiline ones.
Refinements work in string interpolations
- Reason: Step in a consistent effort to make refinements more useful and consistent, working everywhere regular methods work
- Discussion: Feature #13812 (Japanese)
- Code:
module Ref refine Time do def to_s "#{hour}:#{min} at #{month}/#{day}/#{year}" end end end using Ref puts "Now is #{Time.now}" # Ruby 2.4: "Now is 2019-05-27 17:49:27 +0300" -- default to_s # Ruby 2.5: "Now is 17:51 at 5/27/2019" -- refined to_s
- Follow-up: Ruby 2.6 added more features making refinements available in various contexts.
Core classes and modules
Kernel
Note that methods defined in Kernel
are typically available on any object, that’s why these changes are important.
#yield_self
- Reason: Chaining singular value processing the same way
Enumerable
allows chaining of sequence processing methods. - Discussion: Feature #6721
- Code:
[BASE_URL, path].join('/') .yield_self { |url| open(url).read } .yield_self { |body| JSON.parse(body, symbolyze_names: true) } .dig(:data, :items) .yield_self { |items| File.write('response.yml', items.to_yaml) }
- Follow-up: Ruby 2.6 renamed (aliased) unfortunate method name to
#then
#pp
pp
standard library is now automatically required on the first call to Kernel#pp
- Discussion: Feature #14123
#warn
: uplevel:
keyword argument
When uplevel: N
provided to Kernel#warn
, the warning message includes the file and line of the call N
steps up in the callstack.
- Reason: It is sometimes hard to understand exactly what code caused a warning. This feature helps identify the offending caller.
- Discussion: Feature #12882
- Documentation:
Kernel#warn
(it became somewhat documented at Ruby 2.6, but the feature itself has been available since 2.5) - Code:
def that_bad_function warn('do not call me') warn('I am talking to YOU', uplevel: 1) warn('and tell all your clients!', uplevel: 2) end def try_using_it that_bad_function # line 8 end try_using_it # line 11 # Prints: # do not call me # test.rb:8: warning: I am talking to YOU # test.rb:11: warning: and tell all your clients!
#warn
: call Warning.warn
Ruby 2.4 introduced redefinable Warning.warn
method, which allowed to control how warnings are handled. But warn
method wasn’t calling it, so initially only warnings issued from C code could’ve been handled. 2.5 fixes it. (Was not mentioned in NEWS-2.5.0)
- Discussion: Feature #12299#note-14
- Documentation: —
- Code:
def Warning.warn(msg) puts ".warn called with: #{msg.inspect}" end warn 'foo', 'bar' # Ruby 2.4 prints: # foo # bar # Ruby 2.5 prints: # .warn called with: "foo\nbar\n"
- Notes:
Kernel#warn
accepts multiple arguments, and joins them with"\n"
to pass toWarning#warn
as a single string.
Module
: methods for defining methods and accessors became public
- Reason: The methods affected are frequently used in metaprogramming, which prior to 2.5 required reopening classes/modules or using
send
- Discussion: Feature #14132, Feature #14133
- Affected methods:
#attr
,#attr_accessor
,#attr_reader
#attr_writer
,#define_method
,#alias_method
,#undef_method
,#remove_method
. - Code:
Array.define_method(:half_size) { size.to_f / 2 } # => :half_size [1, 2, 3].half_size # => 1.5
Method#===
- Reason:
Method
object can be used in a lot of contexts where theProc
can be used, and this one context (case equality) was lacking. - Discussion: Feature #14142
- Documentation:
Method#===
- Code:
require 'prime' case 137 when Prime.method(:prime?) puts 'prime' else puts 'not!' end # prints 'prime' in Ruby 2.5+, 'not' in Ruby 2.4
Integer
#pow
: modulo argument
- Reason: Modular exponentiation is a useful concept, especially in cryptography, and can be vastly ineffective if performed as to sequential operations.
- Discussion: Feature #12508, Feature #11003
- Documentation:
Integer#pow
- Code:
2120078484650058507891187874713297895455. pow(5478118174010360425845660566650432540723, 5263488859030795548286226023720904036518) # => 4481650795473624846969600733813414725093
#allbits?
, #anybits?
, #nobits?
- Reason: It is noted that testing values against bitmasks in Ruby is a bit non-atomic: first, calculate
value & flags
, and then compare result with zero (no common flags / at least one common flag), or original value (all common flags); the new methods should make the tests clearer - Discussion: Feature #12753
- Documentation:
Integer#allbits?
,Integer#anybits?
,Integer#nobits?
- Code:
CARNIVOROUS = 0b001 MAMMAL = 0b010 TERRASTRIAL = 0b100 lizard = 0b101 lizard.allbits?(CARNIVOROUS | MAMMAL) # => false lizard.anybits?(CARNIVOROUS | MAMMAL) # => true lizard.nobits?(CARNIVOROUS | MAMMAL) # => false
.sqrt
- Reason:
Math.sqrt(x).to_i
for large numbers loses precision, soInteger.sqrt
implements effective and precise integer square root algorithm - Discussion: Feature #13219
- Documentation:
Integer.sqrt
- Code:
Integer.sqrt(10**33) # => 31622776601683793 Integer.sqrt(10**33)**2 # => 999999999999999979762122758866849 Math.sqrt(10**33).to_i # => 31622776601683792 Math.sqrt(10**33).to_i**2 # => 999999999999999916516569555499264
String
#delete_prefix
, #delete_prefix!
, #delete_suffix
, #delete_suffix!
- Discussion: Feature #12694, Feature #13665
- Documentation:
String#delete_prefix
,String#delete_suffix
- Code:
'foo'.delete_prefix('fo') # => 'o' 'bar'.delete_suffix('r') # => 'ba' # Notice that bang-meethods return nil if they've deleted nothing: 'foo'.delete_prefix!('ba') # => nil # This allows to use the methods in ifs: descr = RUBY_DESCRIPTION.dup # => "ruby 2.5.1p57 (2018-03-29 revision 63029) [x86_64-linux]" if descr.delete_prefix!('ruby ') puts "We are running on MRI #{descr}" # => "We are running on MRI 2.5.1p57 ...." end
#each_grapheme_cluster
and #grapheme_clusters
- Reason: In modern Unicode, lots of on-screen characters (graphemes) are represented by several characters, which are logically combined into “grapheme clusters”; now Ruby has a proper way to work with those concepts.
- Discussion: Feature #13780
- Documentation:
String#each_grapheme_cluster
,String#grapheme_clusters
- Code:
"🏳️🌈".length # => 4 "🏳️🌈".chars # => ["🏳", "️", "", "🌈"] "🏳️🌈".grapheme_clusters # => ["🏳️🌈"] "🏳️🌈".each_grapheme_cluster.map { |s| CGI.escape(s) } # => ["%F0%9F%8F%B3%EF%B8%8F%E2%80%8D%F0%9F%8C%88"]
#undump
The inverse of String#dump
which escapes all non-printable and special characters.
- Discussion: Feature #12275
- Documentation:
String#undump
- Code:
p <<~STR This is "fine". Or is it? STR # Prints "This is \"fine\".\nOr is it?\n" # ^ Now, this is dumped string, including quotes around. # How to get back to "nice" string? puts '"This is \"fine\".\nOr is it?\n"'.undump # Prints # This is "fine". # Or is it?
#casecmp
and #casecmp?
return nil
for non-string arguments
Previously, those methods raised TypeError
on arguments that can’t be coerced to String
, now they just return nil
- Reason: The behavior made consistent with
==
and<=>
, as the methods are case-agnostic versions of those operators: when you compare incomparable things, you just get no result at all. - Discussion: Feature #13312
- Documentation:
String#casecmp
,String#casecmp?
- Code:
"Foo".casecmp?(:foo) # Ruby 2.4: TypeError: no implicit conversion of Symbol into String # Ruby 2.5: nil
#start_with?
accepts a regexp
string.start_with?(/foo/)
is an equivalent of string.match?(/\Afoo/)
- Discussion: Feature #13712
- Documentation:
String#start_with?
(introduced in 2.5, but documented in 2.6) - Note: While looking a symmetric method,
#end_with?
does not support regexp; the possibility was discussed in the feature proposal and it was general agreement that the behavior should be symmetric, but eventually the implementation turned out to be too hard.
String#-@
optimized for memory preserving
String#-
(freezing the string) is optimized to deduplicate frozen object
- Discussion: Feature #13077, Feature #13295
- Documentation:
String#-@
- Code:
str = String.new('not frozen') 10.times.map { -str }.map(&:object_id) # Ruby 2.4: 10 different ids => 10 different frozen string objects # Ruby 2.5: 10 same ids => always the same frozen object 5.times.map { -'test' }.map(&:object_id) # Ruby 2.4: 5 different ids => 5 different frozen string objects # Ruby 2.5: 5 same ids => always the same frozen object
Regexp
: absence operator
The underlying Onigmo Regexp library was updated, which brought new operator: (?~foo)
means “match any string that do not ends with foo
”
- Documentation: —
- Code:
# Classic example of usefulness: matching C comments: code = <<~C /* This is a comment. It ends here: */ // And not here: */ C code.scan(%r{/\*.+\*/}m) # => ["/* This is a comment.\nIt ends here: */\n// And not here: */"] code.scan(%r{/\*(?~\*/)\*/}) # => ["/* This is a comment.\nIt ends here: */"]
Struct
with keyword arguments
Struct
can be created with keyword_init: true
, and then its instance will require keyword arguments to initialize
- Discussion: Feature #11925
- Documentation:
Struct.new
- Code:
Person = Struct.new(:first, :last, :age) ModernPerson = Struct.new(:first, :last, :age, keyword_init: true) Person.new('Bob', 'Jones', 40) # => #<struct Person first="Bob", last="Jones", age=40> ModernPerson.new(first: 'Meredith', last: 'Williams', age: 28) # => #<struct ModernPerson first="Meredith", last="Williams", age=28> ModernPerson.new('Meredith', 'Williams', 28) # ArgumentError (wrong number of arguments (given 3, expected 0))
- Follow-ups:
- Ruby 3.1 introduced a method to check whether some struct should be initialized with keyword arguments, and a warning on erroneous initialization of non-keyword-args struct with a hash.
- Ruby 3.2 allowed all structs without explicit
keyword_init:
parameter specified to be initialized by both positional and keyword args.
Time.at
: units
Time.at
second argument was meant to be microseconds, but in 2.5+ it can be micro-, milli- or nano-seconds, specified by third argument: :millisecond
, :usec
or :microsecond
(default), :nanosecond
- Reason: It was noted that when you have nanoseconds value (available as
Time#nsec
) and want to pass it to.at
, it is inconvenient to divide it by1000
. - Discussion: Feature #13919
- Documentation:
Time.at
- Code:
nanoseconds = 123456789 # The only way previously to create time with nsec value Time.at(946684800, nanoseconds / 1000.0).nsec # => 123456789 # New way Time.at(946684800, nanoseconds, :nanosecond).nsec # => 123456789 # Also convenient for some contexts: .123 seconds Time.at(946684800, 123, :millisecond).usec # => 123000
Collections
Enumerable#any?
, #all?
, #none?
, and #one?
accept patterns
Any argument that responds to #===
may now be passed (akin to grep
)
- Discussion: Feature #11286
- Documentation:
Enumerable#all?
,Enumerable#any?
,Enumerable#none?
,Enumerable#one?
- Code:
[1, 2, nil].all?(Numeric) # => false ['America', 'Europe', 'Africa', 'Asia'].any?(/^E/) # => true (1..20).one?(20..30) # => true require 'set' [:foo, :bar, :baz].none?(Set[:foo, :test]) # => false
Array#append
and #prepend
Just aliases for #push
and #unshift
- Reason: The old methods are symmetric with
#pop
and#shift
, respectively, but asymmetric with each other and (especially#unshift
) hard to guess and remember - Discussion: Feature #12746
- Documentation:
Array#append
,Array#prepend
- Code:
ary = [1, 2, 3] ary.append(4) # => [1, 2, 3, 4] ary.prepend(0) # => [0, 1, 2, 3, 4] ary # => [0, 1, 2, 3, 4]
Hash#transform_keys
and #transform_keys!
- Discussion: Feature #13583
- Documentation:
Hash#transform_keys
,Hash#transform_keys!
- Code:
{a: 1, b: 2}.transform_keys { |sym| sym.to_s.capitalize } # => {"A"=>1, "B"=>2} {a: 1, b: 2}.transform_keys(&:to_s) # => {"a"=>1, "b"=>2} {a: 1, b: 2}.transform_keys(&:length) # non-unique - the last value is taken # => {1=>2} {a: 1, b: 2}.transform_keys # without block - returns enumerator # => #<Enumerator: {:a=>1, :b=>2}:transform_keys> # The latter can be useful when modified by .with_index {a: 1, b: 2}.transform_keys.with_index(1) { |k, i| "#{i}: #{k}" } # => {"1: a"=>1, "2: b"=>2}
- Follow-up: Since Ruby 2.5.1, the behavior of
transform_keys!
(bang version) is changed: Ruby 2.5.0 changes the hash as it goes (the same behavior as the old ActiveSupport method of the same name), whereas Ruby 2.5.1 first calculates the new hash and then replaces the old one:# Ruby 2.5.0, Ruby 2.4 with ActiveSupport h = {1 => :hello, 2 => 'world'} h.transform_keys!(&:succ) # first, 2 => :hello replaces 2 => 'world', then the same key is processed again # => {3=>:hello} # Ruby 2.5.1 h = {1 => :hello, 2 => 'world'} h.transform_keys!(&:succ) # => {2=>:hello, 3=>"world"}
Discussion: Bug #14380
- Follow-up: In Ruby 3.0, a new form
transform_keys(from_name: :to_name)
was introduced.
Hash#slice
- Discussion: Feature #13563
- Documentation:
Hash#slice
- Code:
h = { a: 100, b: 200, c: 300 } h.slice(:a) #=> {:a=>100} h.slice(:b, :c, :d) #=> {:b=>200, :c=>300}
- Follow-up: Funnily enough, what started at #8499 as discussing of importing
#slice
/#slice!
/#except
and#except!
from ActiveSupport, ended up in a several tickets:#slice
(current one, accepted for 2.5),#slice!
(#15863, rejected by Matz, “no real-life use-case”), and#except
(#15822, recent ticket, not merged as of May 2019). UPD: Finally made its way to Ruby 3.0.
Exceptions
Backtrace and error message in reverse order
New behavior is activated if STDERR
is TTY
- Discussion: Feature #8661
- Code:
$ cat test.rb {a: 1}.fetch(:b) $ ruby test.rb # Ruby 2.4: test.rb:1:in `fetch': key not found: :b (KeyError) from test.rb:1:in `<main>' # Ruby 2.5: Traceback (most recent call last): 1: from test.rb:1:in `<main>' test.rb:1:in `fetch': key not found: :b (KeyError)
- Follow-up: Feature is tagged “experimental” in Ruby 2.5, somewhat limited in 2.6, and completely reverted in 3.0.
Exception#full_message
Exception#full_message
provides the same string Ruby will print if exception is not caught.
- Discussion: Feature #14141
- Documentation:
Exception#full_message
- Code:
# test.rb begin {a: 1}.fetch(:b) rescue => e puts e.full_message end # `ruby test.rb` prints: # Traceback (most recent call last): # 1: from test.rb:2:in `<main>' # test.rb:2:in `fetch': key not found: :b (KeyError) # # `ruby test.rb 2>err.log` prints: # test.rb:2:in `fetch': key not found: :b (KeyError) # from test.rb:2:in `<main>
- Notice: The format is different depending of whether
STDERR
is TTY or not (even if you printing the returned string toSTDOUT
or just storing it in the variable) - Follow-up: Ruby 2.6 added options for
#full_message
for enforcing desired format regardless of whetherSTDERR
is TTY
KeyError#receiver
and #key
- Discussion: Feature #12063
- Documentation:
KeyError
- Code:
begin {a: 1}.fetch(:b) rescue => e p e.receiver # => {:a=>1} p e.key # => :b end
- Follow-up: Since Ruby 2.6,
receiver:
andkey:
could also be passed when creatingKeyError
in your code (as of Ruby 2.5, only C code could’ve create “proper”KeyError
)
New class: FrozenError
- Discussion: Feature #13224
- Documentation:
FrozenError
- Code:
a = [1, 2, 3].freeze a << 4 # Ruby 2.4: RuntimeError: can't modify frozen Array # Ruby 2.5: FrozenError (can't modify frozen Array)
Don’t hide coercion errors
Previously, some Numeric
and Range
methods caught errors when the provided values were incompatible, and thrown their own. This was hard to debug, so now the original error is thrown instead.
- Discussion: Feature #7688
- Affected methods:
Numeric#step
,#<
,#>
,#>=
,#<=
(whenother.coerce
method fails),Range#initialize
(when<=>
method of range’s ends fail) - Code:
class MyString def initialize(str) @str = str end def <=>(other) # This will fail: we forgot to define attr_reader :str str <=> other.str end include Comparable end 'bar'..MyString.new('foo') # Ruby 2.4: test.rb:13:in `<main>': bad value for range (ArgumentError) # Ruby 2.5: test.rb:7:in `<=>': undefined local variable or method `str' for #<MyString:0x0000556d024e7b08 @str="bar"> (NameError)
Filesystem and IO
IO#pread
and #pwrite
Two atomic methods utilize corresponding system calls to read or write file at specified offset, instead of #seek
+ #read
/#write
- Reason:
- Discussion: Feature #4532
- Documentation:
IO#pread
,IO#pwrite
- Code:
File.write('tmp.txt', 'test me please') f = File.open('tmp.txt') f.pread(2, 5) # => "me" f.pos # => 0, unchanged
IO#write
accepts multiple arguments
- Discussion: Feature #9323
- Documentation:
IO#write
- Code:
$stdout.write('test ', 'me ', 'please') # Prints: test me please
- Notice: Standard library classes that inherit or mimic IO interface were also updated:
StringIO#write
,Zlib::GZipWriter#write
File.open
better supports newline:
option
Somewhat obscurely present IO.new
/IO.read
/File.open
and similar methods options for converting newlines now properly switches file reading to text mode.
- Discussion: Bug #13350
- Documentation: — (it is almost documented by referring from
IO.new
docs toString#encode
docs, where at least longer formnewline_universal: true
is mentioned) - Code:
File.write("crlf.txt", "a\nb\nc", newline: :crlf) # option to convert newlines to Windows format File.binread('crlf.txt') # => "a\r\nb\r\nc" -- what it really wrote File.read('crlf.txt', newline: :universal) # Ruby 2.4: # => "a\r\nb\r\nc" -- newline option is ignored unless encodings passed too # Ruby 2.5: # => "a\nb\nc" -- newline option is respected, file read in text mode & newlines converted
File#path
raises when opened with File::Constants::TMPFILE
option.
The constant TMPFILE
is documented
as “Create an unnamed temporary file”, but before Ruby 2.5, it was still possible to request this file’s path.
- Discussion: Feature #13568
- Documentation:
File#path
- Code:
f = File.open('/tmp', File::RDWR | File::TMPFILE) f.path # Ruby 2.4: => "/tmp" -- what??? # Ruby 2.5: IOError (File is unnamed (TMPFILE?))
- Follow-up: Ruby 3.2 introduced a concept of generic
IO#path
which is allowed to benil
when the path is unknown, so the behavior changed:f = File.open('/tmp', File::RDWR | File::TMPFILE) f.path #=> nil
File.lutime
Same as utime
, but for symlinks sets modification time of link itself, not the referred object
- Discussion: Feature #4052
- Documentation:
File.lutime
Dir.children
and .each_child
- Discussion: Feature #11302
- Documentation:
Dir.children
,Dir.each_child
- Code:
Dir.children('.') # => ["_layouts", ".gitignore", "README.md", "_data", "_src", "_site", "Contributing.md", "_config.yml", "2.6.md", "js", "404.html", ".git", "images", "Gemfile", "css", "Gemfile.lock"]
- Follow-up: Ruby 2.6 added instance methods with same behavior
Dir.glob
: base:
argument
- Discussion: Feature #13056
- Documentation:
Dir.glob
- Code:
Dir.glob('*') # => ["_layouts", "README.md", "err.log", "_data", "_src", "_site", "Contributing.md", "_config.yml", "2.6.md", "js", "404.html", "images", "Gemfile", "css", "Gemfile.lock"] Dir.glob('*', base: '_src') # => ["2.5.md", "script", "tmp", "2.6.md"]
Process.last_status
as an alias of $?
- Discussion: Feature #14043
- Documentation:
Process.last_status
Thread#fetch
Much akin to Hash#fetch
, allows to fetch thread-local variable or provide default value
- Discussion: Feature #13009
- Documentation:
Thread#fetch
(feature is present since Ruby 2.5, but explanations added later) - Code:
threads = [Thread.new { Thread.current[:name] = "first" }, Thread.new {}] threads.each(&:join) threads.map { |t| t.fetch(:name, '<not set>') } # => ["first", "<not set>"] threads.last.fetch(:name) # KeyError (key not found: :name) threads.last.fetch(:name) { |key| "#{key} not set at #{Time.now}" } # => "name not set at 2019-05-30 16:50:44 +0300"
RubyVM::InstructionSequence
new methods
Misc
Random.raw_seed
renamed toRandom.urandom
. Discussion: Bug #9569.Data
is deprecated. It was a base class for C extensions, and it’s not necessary to expose in Ruby level. Feature #3072. Follow-up: Fully removed in 3.0; in 3.2, a new class with the same name but different meaning was reintroduced.
Standard library
Data types
BigDecimal
is now behaving more consistently with core numeric classes:BigDecimal.new
is deprecated in favor ofKernel#BigDecimal()
, and#clone
and#dup
methods return the same instance.Matrix
:Matrix.combine
andMatrix#combine
. Discussion: Feature #10903Matrix#hadamard_product
(aliased as#entrywise_product
)
Set
Set#to_s
is an alias of#inspect
. Discussion: Feature #13676puts Set[:foo, :bar] # Ruby 2.4: #<Set:0x00005614c9069e68> # Ruby 2.5: #<Set: {:foo, :bar}>
Set#===
is an alias of#include?
. Discussion: Feature #13801. This allows the use Sets incase
andgrep
:case RUBY_VERSION when Set['2.5.0', '2.5.1', '2.5.2', '2.5.3'] # ... end [:open, :work, :pause, :close, :open, :break].grep(Set[:open, :close]) # => [:open, :close, :open]
Set#reset
(discussion: Feature #6589) allows to ensure set’s internal state consistency after modifying a mutable element of the set:processes = Set[[:open], [:work]] processes.first << :close processes # => #<Set: {[:open, :close], [:work]}> processes.include?([:open, :close]) # => false processes.reset processes.include?([:open, :close]) # => true
Text processing
StringScanner
: methods#size
,#captures
,#values_at
to introspect latest match, consistent with those ofMatchData
. Discussion: Feature #836scanner = StringScanner.new('y = x^2') scanner.scan(/(\w+)\s*=\s*/) # => "y = " scanner.size # number of captures, including entire matched string # => 2 scanner.values_at(0, 1) # 0 is "entire matched sequence", 1 is "first capture" # => ["y = ", "y"] scanner.captures # only captures # => ["y"]
ERB#result_with_hash
added to allow passing local variables via hash instead ofBinding
object. Discussion: Feature #8631require 'erb' ERB.new('x = <%= x %>').result_with_hash(x: 11) # => "x = 11"
Network and Web
open-uri
:URI.open
method defined as an alias to open-uri’sKernel#open
. open-uri’sKernel#open
will be deprecated in future. Follow-up: Deprecation ofKernel#open
and docs forURI.open
were added in Ruby 2.7.Kernel#open
redefinition was fully removed in 3.0.
Net::HTTP
- New
no_proxy
argument toNet::HTTP.new
, allows to specify list of hosts that should be contacted directly, bypassing the proxy. Discussion: Feature #11195 Net::HTTP#proxy_user
andNet::HTTP#proxy_pass
now can be fetched fromENV['http_proxy']
if it contains them and if OS provides multiuser safe environment (read: Unix-based OS). Bug #12921:ENV['http_proxy'] = 'https://me:secret@proxy.server' http = Net::HTTP.new('https://google.com') http.proxy_user # => "me" http.proxy_pass # => "secret"
Net::HTTP#min_version
andNet::HTTP#max_version
allow to specify min/max SSL version to use. Docs of methods with same names in OpenSSL library provide some explanations. Discussion: Feature #9450Net::HTTP::STATUS_CODES
constant (no docs) is added as HTTP Status Code Repository. Discussion: Misc #12935require 'net/http/status' Net::HTTP::STATUS_CODES.fetch(417) # => "Expectation Failed"
WEBrick
- Support for
Proc
(or any other callable) objects as body responses (no docs). Discussion: Feature #855:require 'webrick' server = WEBrick::HTTPServer.new :Port => 8000 trap 'INT' do server.shutdown end server.mount_proc '/' do |req, res| res.chunked = true # or set headers['content-length'] explicitly res.body = proc { |out| out << Time.now.to_s } end server.start
- Server Name Indication support via
:ServerName
option forWebrick::HTTPServer.new
. Discussion: Feature #13729
Ruby development and introspection
- Coverage: support branch coverage and method coverage measurement. Discussion: Feature #13901
Binding#irb
(which is available, if not documented, since Ruby 2.4, meaning “start IRB session in the current scope”), now automatically requiresirb
. Discussion: Bug #13099RbConfig::LIMITS
is added to provide the limits of C types:require 'rbconfig/sizeof' RbConfig::LIMITS['FIXNUM_MAX'] # => 4611686018427387903
Ripper::Filter#state
to tell the state of scanner. Discussion Feature #13686
Misc
Pathname#glob
Discussion: Feature #7360:require 'pathname' Pathname.new('.').glob('*.md') # => [#<Pathname:2.5.md>, #<Pathname:README.md>, #<Pathname:Contributing.md>, #<Pathname:2.6.md>]
SecureRandom.alphanumeric
mathn.rb
removed from stdlib. Feature #10169. It was a library that patched some core classes to make Ruby’s math more “natural” (by providingRational
andComplex
operations where necessary), but it was decided that side-effects of its implicit inclusion were too sudden, and explicit usage of Rational and Complex became much more natural in later Ruby versions.
Large updated libraries
- Psych 3.0.2: No changelog available.
- RDoc 6.0.1: Changelog is outdated.
- Rubygems 2.7.3: Changelog
- IPAddr 1.2.1: Changelog
- OpenSSL 2.1: Changelog.
Libraries promoted to default gems
stdgems.org project has a nice explanations of default and bundled gems concepts, as well as a list of currently gemified libraries.
“For the rest of us” this means libraries development extracted into separate GitHub repositories, and they are just packaged with main Ruby before release. It means you can do issue/PR to any of them independently, without going through more tough development process of the core Ruby.
Libraries extracted in 2.5:
Follow-up:
- 14 more libraries gemified in 2.6;
- 16 more libraries gemified in 2.7, and 6 just dropped from the standard library (including cmath);
- 34 (!) more libraries gemified in 3.0, and 3 more just dropped from the standard library (including sdbm and webrick).