📜 ⬆️ ⬇️

Ruby 2.1 in Detail (Part 3)


Method #singleton_class? for Module / Class

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

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


More logical Module # ancestors

The #ancestors method, called against a meta-class, now returns an array containing, among other things, meta-classes, which makes its behavior more consistent. It also orders the output of meta-classes, but this is done only if the module was connected to the meta-class with prepend (and not include).

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

')
Object # singleton_method

Same as #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) #=> #<Method: Example.test> # doesn't return instance method Example.singleton_method(:test2) #=> #<NameError: undefined singleton method `test2' for `Example'> # doesn't return inherited class method Example.singleton_method(:name) #=> #<NameError: undefined singleton method `name' for `Example'> 


 example = Object.new def example.test end example.singleton_method(:test) #=> #<Method: #<Object:0x007fc54997a610>.test> 


Method # original_name

In the Method and UnboundMethod classes, the #original_name method appeared, which returns the name of the method 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?

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

Hash # reject

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

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

Displays the following warning:

 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, to which the Hash object returns and does not generate a Vorning, 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 against 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 number representation

The Array # pack and String # unpack methods can now work with the byte representation of long numbers using Q_ / Q! 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 Mac OS X file system HFS Plus 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 Numeric own subclasses. This also means that if it is impossible to cast, TypeError will be raised, not ArgumentError, which the truth should not be a problem during the transition, since TypeError is a subclass of ArgumentError.

Binding # local_variable_get / _set / _defined?

In the Binding class, new methods for getting / setting local variables have appeared. This can be useful if you really need to use a named argument that matches a 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 running 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{<h1><%= title %></h1>\n<h2 class="big friendly"><%= subtitle %></h2>} locals = {:title => "Hitchhiker's Guide to the Galaxy", :subtitle => "Don't Panic"} ERB.new(cover).result(make_binding(locals)) #=> "<h1>Hitchhiker's Guide to the Galaxy</h1>\n<h2 class=\"big friendly\">Don't Panic</h2>" 


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

The CGI class has several useful methods for shielding url and html strings. They were transferred 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.

The various classes of the Digest module have a method for creating a digest for the 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

You can now cancel the SMTP transaction by sending a 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 using OpenURI :: Meta, where the new method #metas was added, which returns an array of values ​​if the header was specified 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 


Write to 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 that uses the finalizer (finaliser), which deletes 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 #=> #<File:/tmp/example20140428-16851-15kf046> path = f.path end File.exist?(path) #=> false 


Rinda multicast support

Now Rinda Ring Classes can listen / connect to group addresses.

Below is an example of using Rinda to create a simple service that listens on a group 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 

Registration service:

 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 additional HTTP options for XMLRPC

XMLRPC :: Client # http returns the 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 WHATWG standard

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

URI.decode_www_form no longer perceives ; as a delimiter, & only default delimiter, but you can set the delimiter value using a 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

New method RbConfig :: SIZEOF 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 to Syslog :: Logger

Syslog :: Logger, a Logger-compatible interface for Syslog, has the option of installing a category.

 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 IOError, it 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) #=> #<OpenSSL::BN:0x007fce7a0c56e8> 


The size enumerator.new argument takes any callable object.

Enumerator.new accepts the size argument, which can be either an integer or an object with the #call method. However, before 2.0.0, the method actually worked only with integers and Proc objects. This is now 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 


Removed curses library

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

Class methods in TSort

The TSort class can be useful in determining the order in which tasks are executed from a list of dependencies. However, using it is rather troublesome, for the 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. Methods that are available as instance methods are now available in the module itself, taking two callable objects, one as a replacement for #tsort_each_node, and the other for #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) 

Displays:

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


TCP Fast Open

Ruby 2.1 adds support for TCP Fast Open , 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) 

Customer:

 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 

The first part The second part

Source: https://habr.com/ru/post/223521/


All Articles