Ruby 2.1の詳細(パート2)


絞り込み

リファインメント(リファインメント)は実験的な機能ではなく、変動を表示しません。また、使用をより便利にするいくつかの詳細が実装に追加されました。

現在、モジュール内でアクティブ化するための##メソッドが、ファイルレベルで絞り込みをアクティブ化するための#usingメソッドに追加されています。 ただし、絞り込みの使用は、まだ字句スコープによって制限されています。 モジュールが再び開かれると、それらはアクティブになりません。

module NumberQuery refine String do def number? match(/\A(0|-?[1-9][0-9]*)\z/) ? true : false end end end module Example using NumberQuery "42".number? #=> true end module Example "42".number? #=> #<NoMethodError: undefined method `number?' for "42":String> end 

精製宣言は、モジュール#includeを使用して継承されるようになりました。 異なるモジュールで定義された絞り込みを1つにグループ化し、このモジュールに対してのみ#usingを呼び出すことにより、それらをすべてアクティブにすることができます。

 module BlankQuery refine Object do def blank? respond_to?(:empty?) ? empty? : false end end refine String do def blank? strip.length == 0 end end refine NilClass do def blank? true end end end module NumberQuery refine Object do def number? false end end refine String do def number? match(/\A(0|-?[1-9][0-9]*)\z/) ? true : false end end refine Numeric do def number? true end end end module Support include BlankQuery include NumberQuery end class User using Support # ... def points=(obj) raise "points can't be blank" if obj.blank? raise "points must be a number" unless obj.number? @points = obj end end 


ストリング#スクラブ

無効なバイトを含む文字列を処理するために、Ruby 2.1でString#scrubメソッドが追加されました。

 # create a string that can't be sensibly printed # 'latin 1' encoded string with accented character string = "öops".encode("ISO-8859-1") # misrepresented as UTF-8 string.force_encoding("UTF-8") # and mixed with a UTF-8 character string = "¡#{string}!" 

この方法で意識的に線を作成することはまずありません(少なくともそうすることを望みます)が、これはいくつかの異なるシステムを通過した線で起こります。

そのような「旅」の最終結果しか得られない場合、誤ってエンコードされた文字をすべて回復することはできませんが、少なくとも削除することはできます。

 # replace with 'replacement character' string.scrub #=> "¡ ops!" # delete string.scrub("") #=> "¡ops!" # replace with chosen character string.scrub("?") #=> "¡?ops!" # yield to a block for custom replacement # (in this case the invalid bytes as hex) string.scrub {|bytes| "<#{bytes.unpack("H*").join}>"} #=> "¡<f6>ops!" 

現在のエンコーディングとinvalid :: replaceを引数として#encodingメソッドを呼び出すと、同じ結果が得られます。

 string.encode("UTF-8", invalid: :replace) #=> "¡ ops!" string.encode("UTF-8", invalid: :replace, replace: "?") #=> "¡?ops!" 


Bignum / Rationalクラスのパフォーマンスの改善

BignumクラスとRationalクラスは、パフォーマンスを向上させるために、GNU Multiple Precision Arithmetic Library(GMP)を使用するようになりました。

レベル4削除$ SAFE

$ SAFE = 4に設定すると、Rubyがサンドボックスモードになり、信頼できないコードの実行が許可されます。 ただし、これは特に効果的ではありませんでした。 インタープリター全体に散在するかなりの量のコードが必要であり、ほとんど使用されなかったため、最終的に削除されました。

 $SAFE = 4 #=> #<ArgumentError: $SAFE=4 is obsolete> 


clock_gettime

Rubyは、さまざまな日付値へのアクセスを提供するProcess.clock_gettimeメソッドを使用してclock_gettime()システム関数にアクセスしました。 最初の引数として、時間IDをメソッドに渡す必要があります。

 Process.clock_gettime(Process::CLOCK_REALTIME) #=> 1391705719.906066 

Process :: CLOCK_REALTIMEを渡すことにより、戻り値としてUnixタイムスタンプを取得します。 また、Time.now.to_fに対応しますが、Timeオブジェクトを作成しないため、少し速く実行されます。

Process.clock_gettimeは、システムクロックの変換に依存しない「単調な」クロックにアクセスするためにも使用できます。 これは、重要な時間測定またはベンチマークに使用できます。

ただし、単調な時計の値は、別の同じマークと比較する場合にのみ意味があります。

 start_time = Process.clock_gettime(Process::CLOCK_MONOTONIC) sleep 1 Process.clock_gettime(Process::CLOCK_MONOTONIC) - start_time #=> 1.0051147330086678 

ベンチマークに使用できる別の値はCLOCK_PROCESS_CPUTIME_IDです。 モノトーン時計と同じように機能します。 常に増加していますが、違いは、CPUが何らかの作業を行ったときにのみ増加することです。 このマークは、別の同様のマークと比較した場合にのみ意味があります。

 start_time = Process.clock_gettime(Process::CLOCK_PROCESS_CPUTIME_ID) sleep 1 Process.clock_gettime(Process::CLOCK_PROCESS_CPUTIME_ID) - start_time #=> 0.005225999999999981 

これらの3つのクロック値(実数、単調、およびプロセッサー)は常に使用可能です。 使用するシステムによっては、他の種類の時計にアクセスして、関連するドキュメントを見つけて読むこともできます

特定の種類の時計の可用性を確認するには、対応する定数が定義されていることを確認するだけで十分です。

 Process.const_defined?(:CLOCK_PROCESS_CPUTIME_ID) #=> true Process.const_defined?(:CLOCK_THREAD_CPUTIME_ID) #=> false 

Process.clock_getresメソッドも使用できます。これにより、特定の種類のクロックによって提供される解像度を確認できます。

RubyGemsアップデート

RubyGemsの埋め込みバージョンがバージョン2.2に更新されました。 RubyGemsのすべてのBundler機能サポートする作業の一環として、 Gemfile.lockサポートがGemfileサポートに追加されました。

gemインストールの--file(または-g)オプションは依存ファイルを必要としなくなり、Gemfileの存在を自動的に検出します。 gem installは、Gemfile.lockがまだ作成されていない場合も生成し、既に作成されている場合はそこに示されているバージョンを考慮します。

 $ ls Gemfile $ gem install -g Fetching: going_postal-0.1.4.gem (100%) Installing going_postal (0.1.4) $ ls Gemfile Gemfile.lock 

変更の完全なリストは、 履歴ファイルにあります。

廃止された機能を削除しましたRake

組み込みのRakeはバージョン10.1.0に更新され、多くの非推奨機能が削除されました。 Rakeの古いバージョンでは、これらの機能に関する警告が長い間発行されているため、互換性の問題はありません。

詳細が必要な場合は、Rakeバージョン10.0.3および10.1.0の変更点の完全なリストを参照してください。

RDocテンプレートを更新

現在、RubyにはRDocバージョン4.1が含まれています。これには、アクセシビリティの点でいくつかの改善が加えられたデフォルトテンプレートの更新が含まれています。 変更の完全なリストは、 履歴ファイルにあります。

プロセス名

新しいProcess.setproctitleメソッドが追加され、$ 0変数にアクセスせずにプロセス名を設定できるようになりました。 Process.argv0メソッドも追加され、$ 0の元の値が取得されます。

たとえば、次のバックグラウンドスクリプトがあります。

 data.each_with_index do |datum, i| Process.setproctitle("#{Process.argv0} - job #{i} of #{data.length}") process(datum) end 

psを起動すると、次のようなものが表示されます。

 $ ps PID TTY TIME CMD 339 ttys000 0:00.23 -bash 7321 ttys000 0:00.06 background.rb - job 10 of 30 


冷凍シンボル

シンボルオブジェクトは、整数と実数の会社を「フリーズ」(フリーズ)オブジェクトとして構成しています。

 :foo.frozen? #=> true :foo.instance_variable_set(:@bar, "baz") #=> #<RuntimeError: can't modify frozen Symbol> 

この変更は、Rubyの将来のバージョンでそのようなオブジェクトのガベージコレクションを改善するために行われました。

スコープリークを修正

eval、instance_eval、またはmodule_evalを使用して実行される行で引数なしでキーワードprivate、protected、public、またはmodule_functionを使用すると、メソッドのスコープは親スコープに「フロー」します。 以下の例では、fooメソッドはprivateです:

 class Foo eval "private" def foo "foo" end end 

バージョン2.1では、これは修正され、fooが開かれます。

#信頼できない? #taintedのエイリアスになりましたか?

Rubyの初期には、オブジェクトを信頼できないものとしてマークする2つのメソッドセットがありました。 これらは同じように機能しますが、オブジェクトに異なるフラグを設定します。

現在、これらのメソッドは統合され、同じフラグを設定していますが、#taintedの方が望ましいでしょうか? そして会社、そして#untrustedに電話するとき? そして、他の人は渦を巻きます。

 string = "foo" string.untrust string.tainted? #=> true 

警告を表示します

 example.rb:2: warning: untrust is deprecated and its behavior is same as taint 


ラムダで返す

ラムダは、呼び出しがメソッドからではなく、ラムダからの制御を返すという点で、ブロックおよびProcオブジェクトとは異なります。 ただし、lambdaが&で渡され、yieldで呼び出された場合は例外があります。 これは現在修正されています。

 def call_with_yield yield end def test call_with_yield(&lambda {return "hello from lambda"}) "hello from method" end test #=> "hello from method" 

上記の例は、「hello from lambda」をRuby <= 2.0.0に出力します。

インターフェースアドレス

これで、Socket.getifaddrsメソッドを使用して、システム内のネットワークインターフェイスの詳細を取得できます。 Socket :: Ifaddrオブジェクトの配列を返します。

 require "socket" info = Socket.getifaddrs.find do |ifaddr| (ifaddr.flags & Socket::IFF_BROADCAST).nonzero? && ifaddr.addr.afamily == Socket::AF_INET end info.addr.ip_address #=> "10.0.1.2" 


StringScannerでの名前付きグループのサポート

StringScanner#[]は、Symbolオブジェクトを引数として受け取り、対応する名前付きグループの値を返すようになりました。

 require "strscan" def parse_ini(string) scanner = StringScanner.new(string) current_section = data = {} until scanner.eos? scanner.skip(/\s+/) if scanner.scan(/;/) scanner.skip_until(/[\r\n]+/) elsif scanner.scan(/\[(?<name>[^\]]+)\]/) current_section = current_section[scanner[:name]] = {} elsif scanner.scan(/(?<key>[^=]+)=(?<value>.*)/) current_section[scanner[:key]] = scanner[:value] end end data end 


YAML.safe_load

YAML(またはより正確には、基礎となる実装であるPsych)では、safe_loadメソッドが追加されました。 デフォルトでは、TrueClass、FalseClass、NilClass、Numeric、String、Array、およびHashクラスをデシリアライズできます。 他のクラスをデシリアライズするには、それらの安全性が確実な場合、それらを引数として渡す必要があります。

未解決のクラスオブジェクトが渡されると、Psych :: DisallowedClass例外がスローされます。これは、YAML :: DisallowedClassとしてスローすることもできます。

 require "yaml" YAML.safe_load(":foo: 1") #=> #<Psych::DisallowedClass: Tried to load unspecified class: Symbol> YAML.safe_load(":foo: 1", [Symbol]) #=> {:foo=>1} 


ResolvでのMDNSおよびLOCレコードのサポート

DNSライブラリの解決は、マルチキャストDNSルックアップの基本的なサポートを受けています。 継続的なリクエストをサポートせず、サービス検出を実行できませんが、それでも非常に便利な革新です(DNSサービス検出を完全にサポートするには、 dnssd gemを試してください )。

 require "resolv" resolver = Resolv::MDNS.new resolver.getaddress("example.local") #=> #<Resolv::IPv4 10.0.1.2> 

resolv-replaceライブラリリンクを使用すると、RubyのほとんどのネットワークライブラリでmDNS名を使用できます。

 require "resolv-replace" require "net/http" Resolv::DefaultResolver.replace_resolvers([Resolv::Hosts.new, Resolv::MDNS.new]) Net::HTTP.get_response(URI.parse("http://example.local")) #=> #<Net::HTTPOK 200 OK readbody=true> 

また、ResolvはDNS LOCレコードを照会する機能も備えています。

 require "resolv" dns = Resolv::DNS.new # find.me.uk has LOC records for all UK postcodes resource = dns.getresource("W1A1AA.find.me.uk", Resolv::DNS::Resource::IN::LOC) resource.latitude #=> #<Resolv::LOC::Coord 51 31 6.827 N> resource.longitude #=> #<Resolv::LOC::Coord 0 8 37.585 W> 

最後に、Resolvの最後の変更で、Resolv :: DNS#fetch_resourceメソッドを使用して完全なDNSメッセージを受信できるようになりました。

 require "resolv" dns = Resolv::DNS.new dns.fetch_resource("example.com", Resolv::DNS::Resource::IN::A) do |reply, reply_name| reply #=> #<Resolv::DNS::Message:0x007f88192e2cc0 @id=55405, @qr=1, @opcode=0, @aa=0, @tc=0, @rd=1, @ra=1, @rcode=0, @question=[[#<Resolv::DNS::Name: example.com.>, Resolv::DNS::Resource::IN::A]], @answer=[[#<Resolv::DNS::Name: example.com.>, 79148, #<Resolv::DNS::Resource::IN::A:0x007f88192e1c80 @address=#<Resolv::IPv4 93.184.216.119>, @ttl=79148>]], @authority=[], @additional=[]> reply_name #=> #<Resolv::DNS::Name: example.com.> end 


ソケットクラスのエラーメッセージ

エラーメッセージにソケットアドレスが追加されました。

 require "socket" TCPSocket.new("localhost", 8080) #=> #<Errno::ECONNREFUSED: Connection refused - connect(2) for "localhost" port 8080> 


加速ハッシュ#シフト

ハッシュ#シフトのパフォーマンスが大幅に向上し、Ruby 1.9で導入された合理化された挿入と組み合わせて、 LRUキャッシュを実装できます。

 class LRUCache def initialize(size) @size, @hash = size, {} end def [](key) @hash[key] = @hash.delete(key) end def []=(key, value) @hash.delete(key) @hash[key] = value @hash.shift if @hash.size > @size end end 


Queue、SizedQueue、およびConditionVariableクラスのパフォーマンスの改善

Queue、SizedQueue、およびConditionVariableクラスは、C(以前はRubyで実装されていました)で実装することで高速化されました。

タイムアウトでの内部例外のインターセプト

これで、Timeoutクラス内で使用されている例外をキャッチしてブロックの実行を中断することができなくなりました。 これは内部実装の詳細であり、外部例外のタイムアウト::エラーは変更されずにキャッチされます。

 require "timeout" begin Timeout.timeout(1) do begin sleep 2 rescue Exception # no longer swallows the timeout exception end end rescue StandardError => e e #=> #<Timeout::Error: execution expired> end 


多くの

#IntersectメソッドがSetクラスに追加されましたか? および#disjoint? #Intersectメソッド? オブジェクトと引数に少なくとも1つの共通要素がある場合はtrueを返し、そうでない場合はfalseを返します。#disjoint? 逆に動作します。

 require "set" a = Set[1,2,3] b = Set[3,4,5] c = Set[4,5,6] a.intersect?(b) #=> true b.intersect?(c) #=> true a.intersect?(c) #=> false a.disjoint?(b) #=> false b.disjoint?(c) #=> false a.disjoint?(c) #=> true 

Setのもう1つの重要な変更点は、#to_setメソッドが作成されたコピーではなく、オブジェクト自体を返すことです。

 require "set" set = Set["foo", "bar", "baz"] set.object_id #=> 70286489985620 set.to_set.object_id #=> 70286489985620 


合理化されたWEBrick応答処理

これで、WEBrickからのHTTP応答の本文は、#readおよび#readpartialメソッドを使用して任意のオブジェクトに割り当てることができます。 以前は、これらはIOまたはStringオブジェクトのみでした。 次の例では、受信した応答を1秒ごとに10秒間表示するクラスを実装しています。

 require "webrick" class EnumeratorIOAdapter def initialize(enum) @enum, @buffer, @more = enum, "", true end def read(length=nil, out_buffer="") return nil unless @more until (length && @buffer.length >= length) || !fill_buffer; end if length part = @buffer.slice!(0, length) else part, @buffer = @buffer, "" end out_buffer.replace(part) end def readpartial(length, out_buffer="") raise EOFError if @buffer.empty? && !fill_buffer out_buffer.replace(@buffer.slice!(0, length)) end private def fill_buffer @buffer << @enum.next rescue StopIteration @more = false end end server = WEBrick::HTTPServer.new(Port: 8080) server.mount_proc "/" do |request, response| enum = Enumerator.new do |yielder| 10.times do sleep 1 yielder << "#{Time.now}\r\n" end end response.chunked = true response.body = EnumeratorIOAdapter.new(enum) end trap(:INT) {server.shutdown} server.start 


数値#ステップ

Numericクラスの#stepメソッドは、位置引数の代わりに、:およびto:で名前付き引数を取ることができるようになりました。 to:引数はオプションです;指定されない場合、シーケンスは無限になります。 位置引数の場合、これは最初の引数としてnilを指定することで実現できます。

 0.step(by: 5, to: 20) do |i| puts i end 

もたらすでしょう

 0 5 10 15 20 

 0.step(by: 3) do |i| puts i end 0.step(nil, 3) do |i| puts i end 

両方の場合

 0 3 6 9 12 ... and so on 


IO

IO#シークメソッドと定数IO :: SEEK_CUR、IO :: SEEK_ENDおよびIO :: SEEK_SETは、シンボル:CUR 、: END、およびSETオブジェクトを受け入れます

2番目の引数として、IO :: SEEK_DATAおよびIO :: SEEK_HOLE(または:DATAおよび:HOLE)を渡すことができるようになりました。 指定すると、最初の引数が遷移の最小データ/空スペースとして使用されます。

 f = File.new("example.txt") # sets the offset to the start of the next data chunk at least 8 bytes long f.seek(8, IO::SEEK_DATA) # sets the offset to the start of the next empty space at least 32 bytes long f.seek(32, IO::SEEK_HOLE) 

これは、IO.const_defined?(:SEEK_DATA)およびIO.const_defined?(:SEEK_HOLE)を使用して検証できるすべてのプラットフォームでサポートされているわけではありません。

例外をスローせずにIO _nonblockを使用する

IO#read_nonblockおよびIO#write_nonblockメソッドは、名前付き例外:引数を取ることができます。 false(デフォルトではtrue)に設定されている場合、メソッドは例外をスローする代わりにエラー時に対応するSymbolオブジェクトを返します。

 require "socket" io = TCPSocket.new("www.example.com", 80) message = "GET / HTTP/1.1\r\nHost: www.example.com\r\nConnection: close\r\n\r\n" loop do IO.select(nil, [io]) result = io.write_nonblock(message, exception: false) break unless result == :wait_writeable end response = "" loop do IO.select([io]) result = io.read_nonblock(32, exception: false) break unless result next if result == :wait_readable response << result end puts response.lines.first 


IOは、外部ASCII-8BITの場合、内部エンコードを無視します

デフォルトで内部エンコーディングと外部エンコーディングを指定すると、Rubyは外部エンコーディングから内部エンコーディングに変換します。 例外は、外部エンコードがASCII-8BITの場合です。この場合、変換は行われません。

エンコードが引数としてIOメソッドに渡される場合も同じ例外を作成する必要がありますが、そうではなく、変換が実行されました。 バグは修正されました。

 File.read("example.txt", encoding: "ascii-8bit:utf-8").encoding #=> #<Encoding:ASCII-8BIT> 


#includeと#prependが開きました

#includeおよび#prependメソッドが開いています。これは、ModuleクラスとClassクラスに適用されます。

 module NumberQuery def number? match(/\A(0|-?[1-9][0-9]*)\z/) ? true : false end end String.include(NumberQuery) "123".number? #=> true 

 require "bigdecimal" module FloatingPointFormat def to_s(format="F") super end end BigDecimal.prepend(FloatingPointFormat) decimal = BigDecimal("1.23") decimal.to_s #=> "1.23" # rather than "0.123E1" 


3番目の部分では、モジュールクラスとオブジェクトクラスに新しいメソッドが追加され、ネットワーククラスが変更され、標準ライブラリにその他の更新が追加されます。
パート1パート 3

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


All Articles