Ruby 2.1 in detail (Part 3)

Original author: Mat Sadler
  • Transfer

The #singleton_class method? for Module / Class

The #singleton_class? Method has been added to the Module and Class classes, which, as you would expect, returns whether the recipient is a meta-class (singleton)

class Example
  singleton_class?     #=> false
  class << self
    singleton_class?   #=> true
  end
end


More logical Module # ancestors

The #ancestors method, called in relation to the meta class, now returns an array containing meta classes, which makes its behavior more consistent. It also arranges the output of the meta classes, but this only happens if the module has been connected to the meta class using prepend (rather than include).

Object.ancestors.include?(Object)                                   #=> true
Object.singleton_class.ancestors.include?(Object.singleton_class)   #=> true


Object # singleton_method

Similar to #method and #instance_method, but returns only meta class methods

class Example
  def self.test
  end
  def test2
  end
end
# returns class method
Example.singleton_method(:test)    #=> #
# doesn't return instance method
Example.singleton_method(:test2)   #=> #
# doesn't return inherited class method
Example.singleton_method(:name)    #=> #


example = Object.new
def example.test
end
example.singleton_method(:test)   #=> #.test>


Method # original_name

The #original_name method has appeared in the Method and UnboundMethod classes, which returns the method name without an alias.

class Example
  def foo
    "foo"
  end
  alias bar foo
end
example = Example.new
example.method(:foo).original_name            #=> :foo
example.method(:bar).original_name            #=> :foo
Example.instance_method(:bar).original_name   #=> :foo


Mutex # owned?

Mutex # owned method ? it is no longer experimental, in general there is nothing more to say about it.

Hash # reject

Calling the Hash # reject method in a subclass of Hash will output the vorning. In Ruby 2.2, calling #reject in Hash subclasses will return a new Hash object, not a subclass object. Therefore, in preparation for this change, a warning has been added so far.

class MyHash < Hash
end
example = MyHash.new
example[:a] = 1
example[:b] = 2
example.reject {|k,v| v > 1}.class   #=> MyHash

The following warning will be displayed:

example.rb:8: warning: copying unguaranteed attributes: {:a=>1, :b=>2}
example.rb:8: warning: following atributes will not be copied in the future version:
example.rb:8: warning:   subclass: MyHash

In Ruby 2.1.1, a complete change was accidentally included, which the Hash object returns and does not generate corroding, but in 2.1.2 this was fixed back.

Vector # cross_product

The cross_product method has been added to the Vector class.

require "matrix"
Vector[1, 0, 0].cross_product(Vector[0, 1, 0])   #=> Vector[0, 0, -1]


Fixnum / Bignum #bit_length

Calling #bit_length with respect to an integer will return the number of digits needed to represent the number in the binary system.

128.bit_length                   #=> 8
32768.bit_length                 #=> 16
2147483648.bit_length            #=> 32
4611686018427387904.bit_length   #=> 63


pack / unpack and byte representation of numbers

The Array # pack and String # unpack methods can now work with the byte representation of long numbers using the Q_ / Q directives! and q_ / q !.

# output may differ depending on the endianness of your system
unsigned_long_long_max = [2**64 - 1].pack("Q!")   #=> "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF"
signed_long_long_min = [-2**63].pack("q!")        #=> "\x00\x00\x00\x00\x00\x00\x00\x80"
signed_long_long_max = [2**63 - 1].pack("q!")     #=> "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\x7F"
unsigned_long_long_max.unpack("Q!")   #=> 18446744073709551615
signed_long_long_min.unpack("q!")     #=> -9223372036854775808
signed_long_long_max.unpack("q!")     #=> 9223372036854775807


Dir.glob returns compound characters

The HFS Plus file system on Mac OS X uses UTF8-MAC encoding for file names with separated characters, for example, é is represented as e and U + 0301, and not just U + 00E9 ( with some exceptions ). Dir.glob and Dir [] now convert them back to UTF8 strings with compound characters.

File.write("composed_e\u{301}xample.txt", "")
File.write("precomposed_\u{e9}xample.txt", "")
puts Dir["*"].map(&:dump)

"composed_\u{e9}xample.txt"
"example.rb"
"precomposed_\u{e9}xample.txt"


Improved type casting for Numeric # quo

The Numeric # quo method now calls #to_r on the receiver, which should improve behavior when implementing native Numeric subclasses. This also means that if the cast cannot be cast, a TypeError will be raised, not an ArgumentError, which should not become a problem during the transition, because TypeError is a subclass of ArgumentError.

Binding # local_variable_get / _set / _defined?

In the Binding class, methods for obtaining / setting local variables have appeared. This can be useful if you really need to use a named argument that matches the reserved keyword.

def primes(begin: 2, end: 1000)
  [binding.local_variable_get(:begin), 2].max.upto(binding.local_variable_get(:end)).each_with_object([]) do |i, array|
    array << i unless (2...i).any? {|j| (i % j).zero?}
  end
end
primes(end: 10)   #=> [2, 3, 5, 7]

Or if you want to use a hash to set variables, for example when executing a template:

def make_binding(hash)
  b = TOPLEVEL_BINDING.dup
  hash.each {|k,v| b.local_variable_set(k, v)}
  b
end
require "erb"
cover = %Q{

<%= title %>

\n

<%= subtitle %>

} locals = {:title => "Hitchhiker's Guide to the Galaxy", :subtitle => "Don't Panic"} ERB.new(cover).result(make_binding(locals)) #=> "

Hitchhiker's Guide to the Galaxy

\n

Don't Panic

"


CGI class methods are now available from the CGI :: Util module

The CGI class has several useful methods for escaping url and html strings. They have been ported to the CGI :: Util module, which can be included in other classes or scripts.

require "cgi/util"
CGI.escape("hello world!")   #=> "hello+world%21"
include CGI::Util
escape("hello world!")       #=> "hello+world%21"


Digest :: Class.file passes arguments to the constructor

Various classes of the Digest module have a method for creating a digest for a file, this method has been changed, and now it passes to the constructor all the arguments passed except for the file name. Those. instead:

require "digest"
Digest::SHA2.new(512).hexdigest(File.read("example.txt"))   #=> "f7fbba..."

You can write:

require "digest"
Digest::SHA2.file("example.txt", 512).hexdigest             #=> "f7fbba..."


Net :: SMTP # rset

Now you can cancel the SMTP transaction by sending the RSET command using the Net :: SMTP # rset method.

require "net/smtp"
smtp = Net::SMTP.start("some.smtp.server")
notification = "Hi %s,\n ..."
users.each do |user|
  begin
    smtp.mailfrom("noreply@example.com")
    smtp.rcptto(user.email)
    smtp.data(sprintf(notification, user.name))
  rescue
    smtp.rset
  end
end
smtp.finish


open-uri supports duplicate headers

open-uri allows using the Kernel # open method to open resources by URI, and extends the return value with OpenURI :: Meta, where a new #metas method was added, which returns an array of values ​​if the header was set several times, for example, set-cookie.

require "open-uri"
f = open("http://google.com")
f.meta["set-cookie"].class     #=> String
f.metas["set-cookie"].class    #=> Array
f.metas["set-cookie"].length   #=> 2


Writing to a file via Pathname

The #write and #binwrite methods for writing files have been added to the Pathname class.

require "pathname"
path = Pathname.new("test.txt").expand_path(__dir__)
path.write("foo")
path.write("bar", 3) # offset
path.write("baz", mode: "a") # append


Tempfile.create

The Tempfile class now has a method similar to the new method, but instead of returning a Tempfile object using a finaliser to delete the file when the object is garbage collected, the create method passes the File object to the block, after which the file is deleted.

require "tempfile"
path = nil
Tempfile.create("example") do |f|
  f                 #=> #
  path = f.path
end
File.exist?(path)   #=> false


Broadcast Support in Rinda

Rinda Ring classes can now listen / connect to group addresses.

Below is an example of using Rinda to create a simple service listening on a multicast address of 239.0.0.1

require "rinda/ring"
require "rinda/tuplespace"
DRb.start_service
tuple_space = Rinda::TupleSpace.new
server = Rinda::RingServer.new(tuple_space, ["239.0.0.1"])
DRb.thread.join

Service registration:

require "rinda/ring"
DRb.start_service
ring_finger = Rinda::RingFinger.new(["239.0.0.1"])
tuple_space = ring_finger.lookup_ring_any
tuple_space.write([:message_service, "localhost", 8080])
# start messaging service on localhost:8080

Getting the address of the service:

require "rinda/ring"
DRb.start_service
ring_finger = Rinda::RingFinger.new(["239.0.0.1"])
tuple_space = ring_finger.lookup_ring_any
_, host, port = tuple_space.read([:message_service, String, Fixnum])
# connect to messaging service

I had some problems with the string tuple_space = ring_finger.lookup_ring_any and I had to use:

tuple_space = nil
ring_finger.lookup_ring(0.01) {|x| break tuple_space = x}


Setting Advanced HTTP Options for XMLRPC

XMLRPC :: Client # http returns a Net :: HTTP object used by the client to set some configuration options that cannot be set via setters.

client = XMLRPC::Client.new("example.com")
client.http.keep_alive_timeout = 30 # keep connection open for longer
# use client ...


URI.encode_ / decode_www_form updated to standard WHATWG

The URI.encode_www_form and URI.decode_www_form methods have been updated to comply with the WHATWG standard .

URI.decode_www_form no longer takes ;a separator as the &only separator by default, but you can set the separator value using the named argument separator :.

require "uri"
URI.decode_www_form("foo=1;bar=2", separator: ";")   #=> [["foo", "1"], ["bar", "2"]]

URI.decode_www_form can now also successfully decode the output of URI.encode_www_form when its value is nil.

require "uri"
string = URI.encode_www_form(foo: 1, bar: nil, baz: 3)   #=> "foo=1&bar&baz=3"
URI.decode_www_form("foo=1&bar&baz=3")                   #=> [["foo", "1"], ["bar", ""], ["baz", "3"]]


RbConfig :: SIZEOF

The new RbConfig :: SIZEOF method returns the size of C types.

require "rbconfig/sizeof"
RbConfig::SIZEOF["short"]   #=> 2
RbConfig::SIZEOF["int"]     #=> 4
RbConfig::SIZEOF["long"]    #=> 8


Setting the logging category in Syslog :: Logger

Syslog :: Logger, the Logger-compatible interface for Syslog, has the ability to install categories.

require "syslog/logger"
facility = Syslog::LOG_LOCAL0
logger = Syslog::Logger.new("MyApp", facility)
logger.debug("test")


CSV.foreach without block returns an enumerator

CSV.foreach, called without a block as an argument, returns an enumerator, but when used for a long time, this led to an IOError, this was fixed.

require "csv"
enum = CSV.foreach("example.csv")
enum.next   #=> ["1", "foo"]
enum.next   #=> ["2", "bar"]
enum.next   #=> ["3", "baz"]


Openssl bignum

OpenSSL :: BN.new now accepts not only strings, but also integers as arguments.

require "openssl"
OpenSSL::BN.new(4_611_686_018_427_387_904)   #=> #


Size Enumerator.new argument accepts any called object

Enumerator.new accepts the size argument, which can be either an integer or an object with the #call method. However, prior to 2.0.0, the method in fact worked only with integers and Proc objects. Now it is fixed and works as stated in the documentation.

require "thread"
queue = Queue.new
enum = Enumerator.new(queue.method(:size)) do |yielder|
  loop {yielder << queue.pop}
end
queue << "foo"
enum.size   #=> 1


Curses library removed

curses has been removed from the standard library and is available as a gem .

Class Methods in TSort

The TSort class can be useful in determining the execution order of tasks from the list of dependencies. However, using it is quite troublesome, for a total you need to implement a class that includes TSort, as well as the #tsort_each_node and #tsort_each_child methods.

But now TSort has become more convenient to use with, for example, hashes. The methods available as instance methods are now available in the module itself, accepting two called objects, one as a replacement for #tsort_each_node, and the second as #tsort_each_child.

require "tsort"
camping_steps = {
  "sleep" => ["food", "tent"],
  "tent" => ["camping site", "canvas"],
  "canvas" => ["tent poles"],
  "tent poles" => ["camping site"],
  "food" => ["fish", "fire"],
  "fire" => ["firewood", "matches", "camping site"],
  "fish" => ["stream", "fishing rod"]
}
all_nodes = camping_steps.to_a.flatten
each_node = all_nodes.method(:each)
each_child = -> step, &b {camping_steps.fetch(step, []).each(&b)}
puts TSort.tsort(each_node, each_child)

Outputs:

stream
fishing rod
fish
firewood
matches
camping site
fire
food
tent poles
canvas
tent
sleep


TCP Fast Open

Ruby 2.1 adds TCP Fast Open support , if available on your system. To check for its presence in the system, you can check the existence of the constants Socket :: TCP_FASTOPEN and Socket :: MSG_FASTOPEN.

Server:

require "socket"
unless Socket.const_defined?(:TCP_FASTOPEN)
  abort "TCP Fast Open not supported on this system"
end
server = Socket.new(Socket::AF_INET, Socket::SOCK_STREAM)
server.setsockopt(Socket::SOL_TCP, Socket::TCP_FASTOPEN, 5)
addrinfo = Addrinfo.new(Socket.sockaddr_in(3000, "localhost"))
server.bind(addrinfo)
server.listen(1)
socket = server.accept
socket.write(socket.readline)

Client:

require "socket"
unless Socket.const_defined?(:MSG_FASTOPEN)
  abort "TCP Fast Open not supported on this system"
end
socket = Socket.new(Socket::AF_INET, Socket::SOCK_STREAM)
socket.send("foo\n", Socket::MSG_FASTOPEN, Socket.sockaddr_in(3000, "localhost"))
puts socket.readline
socket.close

Part One Part Two

Also popular now: