Ruby 2.3.0 preview 1

来源:互联网 时间:1970-01-01


Ruby 2.3.0 preview 1was announced and, as usual it includes plenty of improvements and new features.

Three new features are highlighted in the announcement email:

did_you_mean.gem Safe navigation operator Frozen String Literal Pragma Did you mean

did_you_mean.gemis an aid to debugging Ruby programs for cases when the error is related to NameErrorand NoMethodError.

When a Ruby program breaks due to one of these exceptions, you will receive help in the form of a suggestion for a class name, method name or variable name.

class Account def initialize @full_name = 'John Doe' end def name full_name endend> account = Acount.newNameError: uninitialized constant AcountDid you mean? Account> account = Account.new> account.namsNoMethodError: undefined method `nams' for #<Account:0x007fb2291db068>Did you mean? name> account.nameNameError: undefined local variable or method `full_name' for #<Account:0x007fb2292b00d8 @full_name="John Doe">Did you mean? @full_name

Simple but handy tool for debugging.

Safe navigation operator

If you are a Rails developer you might have used Object#try in you programs.

Object#tryis used to call a method on an object when this object might be nil. Instead of raising an exception, Object#tryresponds with nil.

@user = [email protected] # this will raise NoMethodError: undefined method `profile' for nil:[email protected](:profile) # this will return nil

If you are not using Rails nor including ActiveSupportin your program, then you are left to test if the caller is nilbefore trying to call a method on it.

@user = [email protected] if [email protected]? # as a shorthand this could be if @user

Ruby 2.3.0 includes a new operator: Safe navigation operator.

Operator &.will work in the same way as Object#tryworks in ActiveSupport.

class Profile attr_accessor :full_name def initialize self.full_name = 'John Doe' endendclass User attr_accessor :profile def build_profile self.profile = Profile.new endend> user = User.new> puts "Full name: #{user&.profile&.full_name}"Full name:> user.build_profile> puts "Full name: #{user&.profile&.full_name}"Full name: John Doe

&.will even work with chained calls.

Frozen String Literal Pragma

Ruby 3.0 might have strings being immutableby default, this is a big change that will cause incompatibility and might make harder to upgrade to Ruby 3.0.

In Ruby, having immutable strings is an opt-in functionality but it requires you to explicitly mark any string with Object#freeze.

Freezing strings that will not change in our programs has the advantage of reducing object allocation and memory usage.

# memory.rbrequire 'get_process_mem'mem = GetProcessMem.newGC.startGC.disable# Not freeze stringsbefore_mem = mem.mbbefore_stats = GC.stat1_000.times { 'hello' }after_stats = GC.statafter_mem = mem.mbdelta_allocated_objects = after_stats[:total_allocated_objects] - before_stats[:total_allocated_objects]delta_memory = after_mem - before_memputs "Without frozen strings, allocated objects: #{delta_allocated_objects} - used memory: #{delta_memory}"# Freeze stringsbefore_mem = mem.mbbefore_stats = GC.stat1_000.times { 'hello'.freeze }after_stats = GC.statafter_mem = mem.mbdelta_allocated_objects = after_stats[:total_allocated_objects] - before_stats[:total_allocated_objects]delta_memory = after_mem - before_memputs "With frozen strings, allocated objects: #{delta_allocated_objects} - used memory: #{delta_memory}"GC.enable# memory.rb> ruby memory.rbWithout frozen strings, allocated objects: 1001 - used memory: 0.0390625With frozen strings, allocated objects: 1 - used memory: 0.0

Ruby 2.3.0 helps you prepare for the string change in Ruby 3.0.

First, it introduces a pragmathat we can use per ruby file to tell Ruby that we want to freeze strings by default: # frozen_string_literal: true

# string_change.rbclass StringChange def change(value) value << ' changed' endend# string_frezze.rb# frozen_string_literal: trueload './string_change.rb'class StringFreeze def hit_change value = 'freeze' new_value = StringChange.new.change(value) puts new_value endendStringFreeze.new.hit_change

Running this program with ruby string_frezze.rbgives you the following error:

$ ruby string_freeze.rb /Users/mariochavez/Development/temp/string_change.rb:4:in `change': can't modify frozen String (RuntimeError) from string_freeze.rb:8:in `hit_change' from string_freeze.rb:14:in `<main>'

string_frezze.rbwhich include the pragma, creates all strings literals frozen but string_change.rbtried to change the string.

For a very small program it is easy to see where the string was created and where there was an attempt to modify it. For larger programs, this might not be that intuitive. This is way the --enable-frozen-string-literal-debugflag was introduced.

If you run the same program with the --enable-frozen-string-literal-debugflag, then the error is more helpful.

$ ruby --enable-frozen-string-literal-debug string_freeze.rb /Users/mariochavez/Development/temp/string_change.rb:4:in `change': can't modify frozen String, created at string_freeze.rb:7 (RuntimeError) from string_freeze.rb:8:in `hit_change' from string_freeze.rb:14:in `<main>'

It not only tells you where the attempt to change the string was done, but also where the string was originally created.

Finally for those who want to run their programs as if they were running on Ruby 3.0 - without the need of pragmato freeze strings - you can run your programs with --enable-frozen-string-literal.

ruby --enable-frozen-string-literal --enable-frozen-string-literal-debug string_freeze.rb Conclusions

Its always nice to receive new Ruby versions more than improvements or performance benefits, but also with new features and functionality. As Matz mentioned during his RubyConf 2014 keynote:

We have to feed the sharks, if not they will go away for new shining things.

But in this case, is especially important to have the chance to try and start preparing for the future, easing as much as possible, the transition to Ruby 3.0.

If you want to play with Ruby 2.3.0 preview1, in the announce emailyou will find the link to download it. If you are using rbenvand/or ruby-buildyou can use this custom definition to install it.

# 2.3.0-preview1install_package "openssl-1.0.1p" "https://www.openssl.org/source/openssl-1.0.1p.tar.gz#bd5ee6803165c0fb60bbecbacacf244f1f90d2aa0d71353af610c29121e9b2f1" mac_openssl --if has_broken_mac_opensslinstall_package "ruby-2.3.0-preview1" "http://cache.ruby-lang.org/pub/ruby/2.3/ruby-2.3.0-preview1.tar.gz#dc8f9d48392a2bb226df5f4b4fd2074d81af155cdf3f3799139a6e31e012aefe" ldflags_dirs autoconf standard verify_openssl

Just save it to a file, like 2.3.0-preview1, and install it with the following command:

$ rbenv install ./2.3.0-preview1

Enjoy Ruby!



相关阅读:
Top