Ruby 2.6
- Released at: Dec 25, 2018 (NEWS file)
- Status (as of Feb 07, 2023): EOL, latest is 2.6.10
- This document first published: Dec 29, 2018
- Last change to this document: Feb 07, 2023
Note: As already explained in Introduction, this site is dedicated to changes in the language, not the implementation, therefore the list below lacks mentions of lots of important optimization introduced in 2.6, including the whole JIT big thing. That’s not because they are not important, just because this site’s goals are different.
Highlights
- Endless range
#then
“piping” method- Support for timezones in
Time
Proc
composition- Enumerator chaining
RubyVM::AbstractSyntaxTree
Language
Endless range: (1..)
- Discussion: Feature #12912
- Reason/Usage: More convenient
Array
slicing, and idiomatic open-endedcase
conditions - Documentation: Range: Endless ranges
- Code:
# Usage ary = %w[List of words] ary[2..] # => ["words"] case indicator when 1...8 then # ... when 8...15 then # ... when 15.. then # ... end years.grep(2017...) # Details of behavior: (1..).end # => nil (1..).to_a # RangeError (cannot convert endless range to an array) (1..).each { } # hangs forever (1..) == (1...) # => false (1..).size # => Infinity
- Follow-up: “Beginless” range was introduced in 2.7.
Non-ASCII constant names
Constant names may start with a non-ASCII capital letter.
- Discussion: Feature #13770
- Reason: Why not?
else
in exception-handling context
In exception-handling context, else
without any rescue
is now a syntax error
- Discussion: Feature #14606
- Code:
# In Ruby 2.6 it would be SyntaxError: else without rescue is useless [1,2,3].each do p :foo else p :bar end
The code above obviously contains some error (omitted
if
or something), but before Ruby 2.6 the interpreter would not complain, interpreting it as “begin → perform code that might raise → else (if nothing was raised) performing something”, due to lesser-used Ruby feature of allowingelse
in an exception-handling construct.
Refinements: improved visibility
Refinements are now compatible with #public_send
and #respond_to?
, and implicit #to_proc
.
- Reason: This is part of the effort to make great yet neglected feature of refinements behave more naturally;
- Discussions: Feature #14223, Feature #15326, Feature #15327
- Code:
module StringExt refine String do def surround(before, after = before) after + self + before end def to_proc proc { |val| self % val } end end end using StringExt 'foo'.respond_to?(:surround) # => true in 2.6, false in 2.5 'foo'.public_send(:surround, '|') # => "|foo|" in 2.6, NoMethodError in 2.5 (1..3).map(&'%02i') # => ["01", "02", "03"] in 2.6; wrong argument type String (expected Proc) in 2.5
- Follow-up: Ruby 2.7 also made refinements available in
#method
Misc
- Infamous esoteric flip-flop syntax is deprecated finally: Feature #5400.
- Follow-up: Deprecation is reverted in 2.7
Core classes and modules
Kernel
Notice that methods defined in Kernel
are typically available on any object, that’s why these changes are important.
#then
as an alias for #yield_self
- Reason: Since the introduction of
Kernel#yield_self
at Ruby 2.5, it was pointed out that the name chosen is too long for this basic method, and, unlike most of other core methods, says “how it is implemented” not the intention; after lots of discussion it was decided#then
corresponds best to the method’s goal. - Notice: There is a controversy in the community about this alias, pointing out the fact that
#then
is a typical method name for promises. - Discussion: Feature #14594
- Code:
[BASE_URL, path].join('/') .then { |url| open(url).read } .then { |body| JSON.parse(body, symbolyze_names: true) } .dig(:data, :items) .then { |items| File.write('response.yml', items.to_yaml) }
<Numeric>()
methods have exception:
argument
- Reason: As of Ruby 2.5,
Integer('x')
will raiseArgumentError (invalid value for Integer(): x)
, but in a lot of cases a sane thing to desire is “convert it, if possible” - Discussion: Feature #12732
- Affected methods:
#Integer
,#Float
,#Rational
,#Complex
and#BigDecimal
(stdlib; previously known asBigDecimal.new
) - Documentation:
Kernel#Integer
,Kernel#Float
,Kernel#Rational
,Kernel#Complex
,Kernel#BigDecimal
- Code:
Integer('x') # => ArgumentError (invalid value for Integer(): "x") Integer('x', exception: false) # => nil
#system
has exception:
argument
With exception: true
, the method raises instead of returning false
on non-0 exit code, or nil
on command execution failure.
- Discussion: Feature #14386
- Documentation:
Kernel#system
(unfortunately, seems documentation haven’t been updated with new feature) - Code:
system('cat nonexistent.txt') # => false system('ctat nonexistent.txt') # => nil system('cat nonexistent.txt', exception: true) # RuntimeError (Command failed with exit 1: cat) system('ctat nonexistent.txt', exception: true) # Errno::ENOENT (No such file or directory - ctat)
Module#method_defined?
: inherit
argument
Module#method_defined?
and similar methods accept an optional second argument. If it is false
, only module’s own methods would be returned.
- Reason: Other module introspection methods, like
#methods
, already have similar arguments. It may be important for meta-programming; like “when this module is included, it will redefine some host’s methods, but only those that belong to host”, or “test that all descendants of some abstract class redefine required methods”. - Affected methods:
#method_defined?
,#private_method_defined?
,#protected_method_defined?
,#public_method_defined?
. - Discussion: Feature #14944
- Documentation:
Module#method_defined?
,Module#private_method_defined?
,Module#protected_method_defined?
,Module#public_method_defined?
- Code:
Array.method_defined?(:chunk) # => true Array.method_defined?(:chunk, false) # => false -- it is Enumerable's method Array.method_defined?(:to_h, false) # => true -- despite inheriting from Enumerable, Array redefines it for performance
- Follow-up: In 2.7,
inherit
argument was also added toautoload?
String#split
with block
- Reason: When parsing a huge string, and each substring is used exactly once, it could be ineffective to first create the whole array of parts, and only then iterate through it; with block form, parts are just yielded one by one;
- Discussion: Feature #4780
- Documentation:
String#split
- Code:
"several\nlong\nlines".split("\n") { |part| puts part if part.start_with?('l') } # prints: # long # lines # => "several\nlong\nlines"
Note that in block form, the original string will be returned, not the result of processing parts with block. To work with split results in a method-chaining style, one can utilize
Object#to_enum
:"several\nlong\nlines" .to_enum(:split, "\n") # => Makes a enumerator, yielding each entry from split("\n") .each_with_object(Hash.new(0)) { |ln, h| h[ln.length] += 1 } # => {7=>1, 4=>1, 5=>1}
Time
: support for timezones
The concept of a “timezone object” is introduced for various Time
methods. Ruby does not define any timezone classes by itself, but the API expected corresponds to that of TZInfo::Timezone:
A timezone argument must have
local_to_utc
andutc_to_local
methods, and may havename
andabbr
methods.
- Methods affected:
.new
(timezone may be passed as a last argument, which previously accepted only raw UTC offsets).at
(new keywordin:
argument)#getlocal
(accepts timezone where previously only UTC offset was accepted)#+
,#-
,#succ
(no new argument, but preserve timezone of the source)
- Discussion: Feature #14850
- Documentation: Time: Timezone argument
- Reason: Named timezones are more complicated than just “offset from UTC”. Going over DST date, or between different years in country history, time could have the same timezone, but different UTC offset.
- Code:
zone = TZInfo::Timezone.get('America/New_York') time = Time.new(2018, 6, 1, 0, 0, 0, zone) time.zone # => #<TZInfo::DataTimezone: America/New_York> time.strftime('%H:%M %Z') # => "00:00 EDT" time.utc_offset # => -14400 = -4 hours time += 180 * 24*60*60 # + 180 days, summery->winter transition time.utc_offset # => -18000, -5 hours -- daylight saving handled by timezone
- Follow-up: In Ruby 3.1, the new ways for handier constructing time with timezones were introduced.
Proc
composition
Proc
and Method
classes now have #>>
and #<<
methods for functional composition.
- Reason: This was a long-anticipated feature for moving Ruby towards more functional code.
- Discussion: Feature #6284
- Code:
plus = ->(x, y) { x + y } mul2 = ->(x) { x * 2 } stringify = :to_s.to_proc (plus >> mul2).call(5, 6) # => 22 (mul2 >> stringify).call(5) # (5 * 2).to_s # => "10" (mul2 << stringify).call(5) # 5.to_s * 2 # => "55" # Realistic examples: # 1. Providing chain of functions instead of chaining map's: URLS.map(&Faraday.method(:get) >> :body.to_proc >> JSON.method(:parse) >> ->(data) { data.dig('response', 'items')}) # 2. Storing chain of processings in constant: RESPONSE_PROCESSOR = Faraday.method(:get) >> :body.to_proc >> JSON.method(:parse) >> ->(data) { data.dig('response', 'items')} # ...later... URLS.map(&RESPONSE_PROCESSOR) # 3. Utilizing block-based DSLs (Sinatra-alike) get '/my_endpoint', &parse_params >> perform_business_action >> render_response
- Important notice: Unlike any other places in Ruby (where objects are coerced into procs with
#to_proc
method), “what can be chained” is decided by existence of#call
method; this means you CAN’T chain symbols (notice:body.to_proc
above), but CAN chain some “command pattern” classes withMyClass.call
API.
Array#union
and Array#difference
Array#union
is like |
, but also accepts multiple arguments; Array#difference
is like -
, but accepts multiple arguments.
- Discussion: Feature #14097
- Reason: Multiple-argument methods are more effective, and better chainable than operator form
- Documentation:
Array#union
,Array#difference
- Code:
[1, 2, 3].union([3, 4], [4, 5]) # => [1, 2, 3, 4, 5] [1, 2, 3, 4].difference([3, 4], [1]) # => [2]
- Notice: There are also plans (discussed in the same ticket above) to introduce mutating
union!
form. - Follow-up: Ruby 2.7 added
#intersection
method.
Hash#merge
with multiple arguments
- Discussion: Feature #15111
- Methods affected:
Hash#merge
,Hash#merge!
,Hash#update
(alias for#merge!
) - Documentation:
Hash#merge
- Code:
{a: 1, b: 2}.merge({b: 3, c: 4}, {c: 5, b: 6}) # => {a: 1, b: 6, c: 5} {a: 1, b: 2}.merge({b: 3, c: 4}, {c: 5, b: 6}) { |key, oldval, newval| [oldval, newval] } # => {a: 1, b: [[2, 3], 6], c: [4, 5]}
Note the last example: if the block is provided for conflict resolution, it is called repeatedly for each pair of values provided for the conflicting key (not
|key, *all_conflicting_values|
as one may expect).
Enumerables
#filter
/#filter!
#select
is aliased as #filter
(and #select!
as #filter!
, where applicable).
- Reason: It was argued for a long time that most of other languages name the concept “filter”, so the new alias was added to lessen the confusion for newcomers.
- Discussion: Feature #13784
- Classes and modules affected:
Enumerable
,Enumerator
,Enumerator::Lazy
,Struct
(only#filter
),Array
,Hash
, andSet
of standard library (#filter
and#filter!
).
#to_h
with a block
- Reason: It was noted that
.map { |...| [some_key, some_value] }.to_h
is a very common pattern and should be made DRY; - Discussion: Feature #15143;
- Classes and modules affected:
Enumerable
and everything that includes it; - Documentation:
Enumerable#to_h
,Array#to_h
,ENV.to_h
,Hash#to_h
,Struct#to_h
- Code:
{a: 1, b: 2, c: 3}.to_h { |k, v| [k.to_s, -v] } # => {'a' => -1, 'b' => -2, 'c' => -3} File.readlines('test.txt').each_with_index.to_h { |l, i| [i, l] } # => {0 => 'first line', 1 => 'second line', 2 => 'third line'}
Enumerator::ArithmeticSequence
Range#step
and Numeric#step
are now returning not an instance of Enumerator
, but Enumerator::ArithmeticSequence
instead.
- Reason/Usage: This feature was added by request of scientific Ruby community; it is useful for reusing results of
(from..to).step(s)
as a value object for idiomatic slicing of custom collections, see alsoRange#%
. - Discussion: Feature #13904
- Documentation:
Enumerator::ArithmeticSequence
,Range#step
,Numeric#step
- Code:
# Basic usage remains the same: (1..10).step(2).to_a # => [1, 3, 5, 7, 9] enum = (1..10).step(2) # => ((1..10).step(2)) enum.class # => Enumerator::ArithmeticSequence enum.class.ancestors # => [Enumerator::ArithmeticSequence, Enumerator, Enumerable ...] # So, it is just a specialized subclass of enumerator, adding this methods: enum.begin # => 1 enum.end # => 10 enum.step # => 2
- Follow-up: In 3.0,
Array
slicing withArithmeticSequence
(from 1st to 10th, each 2nd element) became possible.
Enumerator
chaining
Several enumerators can now be chained into one with Enumerator#+(other)
or Enumerable#chain(list, of, enumerators)
. The result of the operation is Enumerator::Chain
(specialized subclass of Enumerator
).
- Reason: Cleaner expression of chaining enumeration for several sequences, especially for lazy enumerators
- Discussion: Feature #15144
- Documentation:
Enumerator#+
,Enumerable#chain
,Enumerator::Chain
- Code:
[1, 2, 3].chain # => #<Enumerator::Chain: [[1, 2, 3]]> [1, 2, 3].each + [4, 5, 6].each # => #<Enumerator::Chain: [#<Enumerator: [1, 2, 3]:each>, #<Enumerator: [4, 5, 6]:each>]> [1, 2, 3].chain([4, 5, 6]) # => #<Enumerator::Chain: [[1, 2, 3], [4, 5, 6]]> # Realistic use-case: # Take data from several sources, abstracted into enumerator, fetching it on demand sources = URLS.lazy.map { |url| open(url).read } .chain(LOCAL_FILES.lazy.map { |path| File.read(path) }) # ...then uniformely search several sources (lazy-loading them) for some value sources.detect { |body| body.include?('Ruby 2.6') }
Range
Range#===
uses #cover?
instead of #include?
- Reason: Previously, case equality operator used
#include?
, which underneath iterates through entire range (except for Numerics). With objects other than numbers it could be ineffective (creating thousands of objects), impossible (if there is no notion of “next object”, but exists notion of order) or imprecise. - Discussion: Feature #14575
- Code:
case DateTime.now when Date.today..Date.today + 1 # this would've not been reached before Ruby 2.6 end # this would raise "can't iterate from Gem::Version" before Ruby 2.6 case Gem::Version.new('2.4') when Gem::Version.new('1.8.7')..Gem::Version.new('2.5') end gem 'ruby-ip' require 'ip' # this would perform ~0.5 sec before Ruby 2.6, iterating over 65536-elt sequence case IP.new('192.168.10.4') when IP.new('192.168.0.0')..IP.new('192.168.255.255') end
- Notice: For
String
ranges, behavior left unchanged, so example with versions above would NOT work with pure-String
versions. - Follow-up:
String
behavior was fixed in Ruby 2.7, so in 2.7 this code prints “yes”:case '2.5' when '1.8.7'..'2.6' puts "yes" else puts "no" end
Range#cover?
accepts range argument
- Discussion: Feature #14473
- Code:
(1..5).cover?(2..3) # => true
Range#%
alias
- Reason/Usage: It was proposed to have short syntax of complex array slicing, usable for math algorithms; see also
Enumerable::ArithmeticSequence
. - Discussion: Feature #14697
- Code:
(5..20) % 2 # => ((5..20).%(2)) -- an instance of Enumerable::ArithmeticSequencee some_fancy_collection[(5..20) % 2] # each second element in 5..20 range
Note that
()
around the range is mandatory here, because5..20 % 2
will be parsed as(5)..(20 % 2)
- Notice: Ruby’s
Array
doesn’t support slicing withEnumerable::ArithmeticSequencee
as of 2.6. - Follow-up: In 3.0,
Array
slicing withArithmeticSequence
became possible.
Exceptions
New arguments: receiver:
and key:
NameError
, NoMethodError
accept :receiver
on creation; KeyError
accepts :receiver
and :key
.
- Reason: Since Ruby 2.5, these exception classes were “introspectable”: when you catch them, you can fetch the object that caused the problem and (in case of
KeyError
) problematic key; but there were no way to add that helpful data when raising an exception in your own code. - Discussion: Feature #14313
- Documentation:
NameError.new
,NoMethodError.new
(docs not updated),KeyError.new
- Code:
class MyFancyCollection def [](key) # ... raise KeyError.new("don't have this: #{key}", receiver: self, key: key) # ... end end
- Notice:
<Exception>.new
syntax is the only way to pass new arguments, this would not work:raise KeyError, "don't have this: #{key}", receiver: self, key: key
- Follow-up: Ruby 2.7 adds
receiver:
argument forFrozenError
, too.
Exception#full_message
options
Exception#full_message
(which returns “exceptions how they are printed by Ruby”, including message, class and backtrace) takes :highlight
and :order
options. This means client code can fine-tune which result it wants to achieve.
- Reason: Since introduction of new way for printing exceptions to STDERR (backtrace in reverse order and message is highlighted in bold), there were some improvements proposed to handle different contexts of exception printing.
- Discussion: Bug #14324
- Documentation:
Exception#full_message
- Code:
begin {a: 1}.fetch(:b) rescue => e # highlight: true/false, order: :top/:bottom p e.full_message(highlight: false, order: :top) # "t.rb:2:in `fetch': key not found: :b (KeyError)\n\tfrom t.rb:2:in `<main>'\n" p e.full_message(highlight: true, order: :bottom) # "\e[1mTraceback\e[m (most recent call last):\n\t1: from t.rb:2:in `<main>'\nt.rb:2:in `fetch': \e[1mkey not found: :b (\e[1;4mKeyError\e[m\e[1m)\e[m\n" p e.full_message # Output could be either of two above, depending on whether STDERR is terminal # or redirected with `ruby test.rb 2> err.log end
- Follow-up: In Ruby 3.2, one more related method
#detailed_message
was added.
Exception output tweaking
Exception#cause
is now printed if STDERR is the terminal:begin {a: 1}.fetch(:b) rescue => e raise 'something went wrong' end # Traceback (most recent call last): # 1: from t.rb:2:in `<main>' # t.rb:2:in `fetch': key not found: :b (KeyError) # 1: from t.rb:1:in `<main>' # t.rb:4:in `rescue in <main>': something went wrong (RuntimeError)
Notice the exception that led to
rescue
/raise
block (KeyError
) is printed too.- Discussion: Feature #8257
- Exception backtrace and error message are printed in reverse order when the exception is not caught and STDOUT is unchanged and a tty. Feature #8661
- Follow-up: Reverted completely to pre-2.5 order in 3.0.
Filesystem and IO
Dir#each_child
and Dir#children
- Discussion: Feature #13969
- Documentation:
Dir#each_child
,Dir#children
- Code:
Dir.new('.').children # => ["_layouts", ".gitignore", "README.md", "_data", "_src", "_site", "_config.yml", "2.6.md", "js", "404.html", ".git", "images", "Gemfile", "css", "Gemfile.lock"]
IO open mode: 'x'
When opening a new file for writing, 'wx'
can be specified to request the file does not exist before opening.
- Discussion: Feature #11258
- Documentation: IO: Open mode
- Code:
f1 = File.open('temp.txt', 'wx') # => #<File:temp.txt> f2 = File.open('temp.txt', 'wx') # it is already created by previous statement # Errno::EEXIST (File exists @ rb_sysopen - temp.txt)
Minor changes
Object#=~
is deprecated: Feature #15231 (butNilClass#=~
is still allowed without deprecation notice).Random.bytes
: Feature #4938 /Random.bytes
Random.bytes(5) # => "8\a\xB0\xD1V" # ...will probably return different value for you, though :)
Introspection
Binding#source_location
- Reason: Important usage for this new feature is proper reporting in block-based DSL usage: if something was inconsistent in the passed block, library code may use
block.binding.source_location
to report where exactly problematic code came from. - Discussion: Feature #14230
- Documentation:
Binding#source_location
- Code:
binding.source_location # => ["(irb)", 114] def my_dsl(&block) puts "Evaluating block from #{block.binding.source_location.join(':')}" end my_dsl { ... } # Prints "Evaluating block from (irb):118"
RubyVM.resolve_feature_path
For string name
, returns what path require(name)
will load (without actually loading it).
- Reason: Static analysis of the program (finding the code it will use without really loading it); understanding where dependencies are coming from.
- Discussion: Feature #15230
- Documentation: —
- Code:
RubyVM.resolve_feature_path('net/http') # => [:rb, "<...>/ruby-2.6.0/lib/ruby/2.6.0/net/http.rb"] RubyVM.resolve_feature_path('syslog') # => [:so, "<...>/ruby-2.6.0/lib/ruby/2.6.0/x86_64-linux/syslog.so"] RubyVM.resolve_feature_path('garbage') # LoadError (cannot load such file -- garbage) require 'net/http' RubyVM.resolve_feature_path('net/http') # => [:rb, false] -- for already loaded libraries, the path would not be returned # For gems: RubyVM.resolve_feature_path('faraday') # LoadError (cannot load such file -- faraday) gem 'faraday' # without this, gems can not be deduced RubyVM.resolve_feature_path('faraday') # => [:rb, "<...>/gems/faraday-0.15.4/lib/faraday.rb"]
- Follow-up: In Ruby 2.7,
resolve_feature_path
was moved to a$LOAD_PATH
singleton method, and its behavior for already loaded pathes was fixed to return path nevertheless.
RubyVM::AbstractSyntaxTree
The new module provides an “official” Ruby parser, replacing stdlib Ripper (and third-party parsers). The module is considered experimental, and its API can change in the future versions.
- Documentation:
RubyVM::AbstractSyntaxTree
- Code:
tree = RubyVM::AbstractSyntaxTree.parse('1+2') # => #<RubyVM::AbstractSyntaxTree::Node(SCOPE(0) 1:0, 1:3): > tree.type # => :SCOPE tree.children # => [[], nil, #<RubyVM::AbstractSyntaxTree::Node(OPCALL(36) 1:0, 1:3): >] tree.children[2].type # => :OPCALL tree.children[2].children # => [#<RubyVM::AbstractSyntaxTree::Node(LIT(59) 1:0, 1:1): >, :+, #<RubyVM::AbstractSyntaxTree::Node(ARRAY(42) 1:2, 1:3): >] tree.children[2].children[0].children # => [1]
- Note: One may be excited about
RubyVM::AbstractSyntaxTree.of(proc)
, but it doesn’t mean the “real” extraction of code from liveProc
, rather a simple hack with trying to find proc’s source file and parse it. - Follow-up: In Ruby 3.2, new options were added for
parse
, allowing to:- Perform fault-tolerant parsing (parse syntactically wrong/incomplete code);
- Preserve tokens from source code alongside nodes;
TracePoint
improvements
Ruby TracePoint
API allows to observe any events that are happening during program evaluation. In 2.6, there were several improvements to allow better control on event handling and their introspection.
#parameters
On :b_call
and :c_call
(block and method call events), TracePoint
now provides #parameters
method to fetch call params definitions.
- Discussion: Feature #14694
- Documentation:
TracePoint#parameters
- Code:
t = TracePoint.new(:b_call) { |tp| p [tp.event, tp.parameters] } t.enable [1, 2, 3].map { |x| x.to_s } # [:b_call, [[:opt, :x]]] # [:b_call, [[:opt, :x]]] # [:b_call, [[:opt, :x]]]
- Notice: Format of return value is the same as for
Method#parameters
:script_compiled
event
Event fired when a new piece of Ruby code is loaded (through eval
, require
or load
). #instruction_sequence
on this event will return compiled RubyVM::InstructionSequence
object, and #eval_script
will return the text of the script sent to eval
.
- Discussion: Feature #15287
- Documentation: TracePoint: Events (though new event seems to be omitted),
TracePoint#instruction_sequence
,TracePoint#eval_script
- Code:
t = TracePoint.new(:script_compiled) { |tp| p [tp.event, tp.instruction_sequence, tp.eval_script] } t.enable eval('p 1') # [:script_compiled, <RubyVM::InstructionSequence:<main>@(eval):1>, "p 1"] require 'net/http' # [:script_compiled, <RubyVM::InstructionSequence:<top (required)>@.../ruby-2.6.0/lib/ruby/2.6.0/net/http.rb:0>, nil] # [:script_compiled, <RubyVM::InstructionSequence:<top (required)>@.../ruby-2.6.0/lib/ruby/2.6.0/net/protocol.rb:0>, nil] # [:script_compiled, <RubyVM::InstructionSequence:<top (required)>@.../ruby-2.6.0/lib/ruby/2.6.0/socket.rb:0>, nil] # .....
#enable
: new params target:
and target_line:
With new parameters provided, you can fine-tune for what methods or specific lines to catch events.
- Reason: Less trace garbage and performance footprint on tracing in complex codebase, allowing very specific tracepoints to be enabled even in production.
- Discussion: Feature #15289.
- Documentation:
TracePoint#enable
(not much, though…) - Code:
t = TracePoint.new(:line) { |tp| p tp } def m1 p 1 end def m2 p 2 end t.enable(target: method(:m1)) m1 # prints #<TracePoint:line@test.rb:5 in `m1'> m2 # prints nothing
- Notice: In absence of docs, we can at least point at commit message:
code
should be consisted ofInstructionSequence
(iseq) (RubyVM::InstructionSequence.of(code)
should not return nil). If code is a tree of iseq,TracePoint
is enabled on all of iseqs in a tree. - Follow-ups:
- In 2.7, the docs have emerged, and a new parameter named
target_thread:
was introduced. It was missing from the officialNEWS
-file and therefore missing from this changelog (which is a thing to be fixed!) - In 3.2,
target_thread:
began defaulting to the current thread with block form ofenable
.
- In 2.7, the docs have emerged, and a new parameter named
Standard library
- Bundler is added to Standard Library: Feature #12733.
Kernel#BigDecimal()
method now acceptsexception:
argument (see above for explanations)URI
now can openfile://
URIs,URI::File
class added.- Documentation:
URI::File
- Discussion: Feature #14035
- Documentation:
Net::HTTP
:write_timeout
added- Documentation:
Net::HTTP#write_timeout=
- Discussion: Feature #13396
- Documentation:
Net::HTTPServerError
renamed toNet::HTTPClientException
- Discussion: Bug #14688
FileUtils.cp_lr
added- Documentation:
FileUtils.cp_lr
- Discussion: Feature #4189
- Documentation:
Large updated libraries
- RubyGems 3.0.1: Changelog
- RDoc: Changelog is outdated.
- CSV 3.0.2: Changelog.
- REXML 3.1.9: Changelog.
- Matrix 1.0.0: Changelog.
- RSS: No Changelog available.
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.6:
Follow-up:
- 16 more libraries gemified in 2.7, and 6 just dropped from the standard library;
- 34 (!) more libraries gemified in 3.0, and 3 more just dropped from the standard library (including webrick).