Coverage
provides coverage measurement feature for Ruby. This feature is experimental, so these APIs may be changed in future.
Caveat: Currently, only process-global coverage measurement is supported. You cannot measure per-thread covearge.
require “coverage”
require or load Ruby source file
Coverage.result
will return a hash that contains filename as key and coverage array as value. A coverage array gives, for each line, the number of line execution by the interpreter. A nil
value means coverage is disabled for this line (lines like else
and end
).
[foo.rb] s = 0 10.times do |x| s += x end if s == 45 p :ok else p :ng end [EOF] require "coverage" Coverage.start require "foo.rb" p Coverage.result #=> {"foo.rb"=>[1, 1, 10, nil, nil, 1, 1, nil, 0, nil]}
Coverage
If a coverage mode is not explicitly specified when starting coverage, lines coverage is what will run. It reports the number of line executions for each line.
require "coverage" Coverage.start(lines: true) require "foo.rb" p Coverage.result #=> {"foo.rb"=>{:lines=>[1, 1, 10, nil, nil, 1, 1, nil, 0, nil]}}
The value of the lines coverage result is an array containing how many times each line was executed. Order in this array is important. For example, the first item in this array, at index 0, reports how many times line 1 of this file was executed while coverage was run (which, in this example, is one time).
A nil
value means coverage is disabled for this line (lines like else
and end
).
Coverage
Oneshot lines coverage tracks and reports on the executed lines while coverage is running. It will not report how many times a line was executed, only that it was executed.
require "coverage" Coverage.start(oneshot_lines: true) require "foo.rb" p Coverage.result #=> {"foo.rb"=>{:oneshot_lines=>[1, 2, 3, 6, 7]}}
The value of the oneshot lines coverage result is an array containing the line numbers that were executed.
Coverage
Branches coverage reports how many times each branch within each conditional was executed.
require "coverage" Coverage.start(branches: true) require "foo.rb" p Coverage.result #=> {"foo.rb"=>{:branches=>{[:if, 0, 6, 0, 10, 3]=>{[:then, 1, 7, 2, 7, 7]=>1, [:else, 2, 9, 2, 9, 7]=>0}}}}
Each entry within the branches hash is a conditional, the value of which is another hash where each entry is a branch in that conditional. The values are the number of times the method was executed, and the keys are identifying information about the branch.
The information that makes up each key identifying branches or conditionals is the following, from left to right:
A label for the type of branch or conditional.
A unique identifier.
The starting line number it appears on in the file.
The starting column number it appears on in the file.
The ending line number it appears on in the file.
The ending column number it appears on in the file.
Coverage
Methods coverage reports how many times each method was executed.
[foo_method.rb] class Greeter def greet "welcome!" end end def hello "Hi" end hello() Greeter.new.greet() [EOF] require "coverage" Coverage.start(methods: true) require "foo_method.rb" p Coverage.result #=> {"foo_method.rb"=>{:methods=>{[Object, :hello, 7, 0, 9, 3]=>1, [Greeter, :greet, 2, 2, 4, 5]=>1}}}
Each entry within the methods hash represents a method. The values in this hash are the number of times the method was executed, and the keys are identifying information about the method.
The information that makes up each key identifying a method is the following, from left to right:
The class.
The method name.
The starting line number the method appears on in the file.
The starting column number the method appears on in the file.
The ending line number the method appears on in the file.
The ending column number the method appears on in the file.
Coverage
Modes You can also run all modes of coverage simultaneously with this shortcut. Note that running all coverage modes does not run both lines and oneshot lines. Those modes cannot be run simultaneously. Lines coverage is run in this case, because you can still use it to determine whether or not a line was executed.
require "coverage" Coverage.start(:all) require "foo.rb" p Coverage.result #=> {"foo.rb"=>{:lines=>[1, 1, 10, nil, nil, 1, 1, nil, 0, nil], :branches=>{[:if, 0, 6, 0, 10, 3]=>{[:then, 1, 7, 2, 7, 7]=>1, [:else, 2, 9, 2, 9, 7]=>0}}, :methods=>{}}}
JSON is a lightweight data-interchange format.
A JSON value is one of the following:
Double-quoted text: "foo"
.
Number: 1
, 1.0
, 2.0e2
.
Boolean: true
, false
.
Null: null
.
Array: an ordered list of values, enclosed by square brackets:
["foo", 1, 1.0, 2.0e2, true, false, null]
Object: a collection of name/value pairs, enclosed by curly braces; each name is double-quoted text; the values may be any JSON values:
{"a": "foo", "b": 1, "c": 1.0, "d": 2.0e2, "e": true, "f": false, "g": null}
A JSON array or object may contain nested arrays, objects, and scalars to any depth:
{"foo": {"bar": 1, "baz": 2}, "bat": [0, 1, 2]} [{"foo": 0, "bar": 1}, ["baz", 2]]
To make module JSON available in your code, begin with:
require 'json'
All examples here assume that this has been done.
You can parse a String containing JSON data using either of two methods:
JSON.parse(source, opts)
JSON.parse!(source, opts)
where
source
is a Ruby object.
opts
is a Hash object containing options that control both input allowed and output formatting.
The difference between the two methods is that JSON.parse!
omits some checks and may not be safe for some source
data; use it only for data from trusted sources. Use the safer method JSON.parse
for less trusted sources.
When source
is a JSON array, JSON.parse
by default returns a Ruby Array:
json = '["foo", 1, 1.0, 2.0e2, true, false, null]' ruby = JSON.parse(json) ruby # => ["foo", 1, 1.0, 200.0, true, false, nil] ruby.class # => Array
The JSON array may contain nested arrays, objects, and scalars to any depth:
json = '[{"foo": 0, "bar": 1}, ["baz", 2]]' JSON.parse(json) # => [{"foo"=>0, "bar"=>1}, ["baz", 2]]
When the source is a JSON object, JSON.parse
by default returns a Ruby Hash:
json = '{"a": "foo", "b": 1, "c": 1.0, "d": 2.0e2, "e": true, "f": false, "g": null}' ruby = JSON.parse(json) ruby # => {"a"=>"foo", "b"=>1, "c"=>1.0, "d"=>200.0, "e"=>true, "f"=>false, "g"=>nil} ruby.class # => Hash
The JSON object may contain nested arrays, objects, and scalars to any depth:
json = '{"foo": {"bar": 1, "baz": 2}, "bat": [0, 1, 2]}' JSON.parse(json) # => {"foo"=>{"bar"=>1, "baz"=>2}, "bat"=>[0, 1, 2]}
When the source is a JSON scalar (not an array or object), JSON.parse
returns a Ruby scalar.
String:
ruby = JSON.parse('"foo"') ruby # => 'foo' ruby.class # => String
Integer:
ruby = JSON.parse('1') ruby # => 1 ruby.class # => Integer
Float:
ruby = JSON.parse('1.0') ruby # => 1.0 ruby.class # => Float ruby = JSON.parse('2.0e2') ruby # => 200 ruby.class # => Float
Boolean:
ruby = JSON.parse('true') ruby # => true ruby.class # => TrueClass ruby = JSON.parse('false') ruby # => false ruby.class # => FalseClass
Null:
ruby = JSON.parse('null') ruby # => nil ruby.class # => NilClass
Option max_nesting
(Integer) specifies the maximum nesting depth allowed; defaults to 100
; specify false
to disable depth checking.
With the default, false
:
source = '[0, [1, [2, [3]]]]' ruby = JSON.parse(source) ruby # => [0, [1, [2, [3]]]]
Too deep:
# Raises JSON::NestingError (nesting of 2 is too deep): JSON.parse(source, {max_nesting: 1})
Bad value:
# Raises TypeError (wrong argument type Symbol (expected Fixnum)): JSON.parse(source, {max_nesting: :foo})
Option allow_nan
(boolean) specifies whether to allow NaN
, Infinity
, and MinusInfinity
in source
; defaults to false
.
With the default, false
:
# Raises JSON::ParserError (225: unexpected token at '[NaN]'): JSON.parse('[NaN]') # Raises JSON::ParserError (232: unexpected token at '[Infinity]'): JSON.parse('[Infinity]') # Raises JSON::ParserError (248: unexpected token at '[-Infinity]'): JSON.parse('[-Infinity]')
Allow:
source = '[NaN, Infinity, -Infinity]' ruby = JSON.parse(source, {allow_nan: true}) ruby # => [NaN, Infinity, -Infinity]
Option symbolize_names
(boolean) specifies whether returned Hash keys should be Symbols; defaults to false
(use Strings).
With the default, false
:
source = '{"a": "foo", "b": 1.0, "c": true, "d": false, "e": null}' ruby = JSON.parse(source) ruby # => {"a"=>"foo", "b"=>1.0, "c"=>true, "d"=>false, "e"=>nil}
Use Symbols:
ruby = JSON.parse(source, {symbolize_names: true}) ruby # => {:a=>"foo", :b=>1.0, :c=>true, :d=>false, :e=>nil}
Option object_class
(Class) specifies the Ruby class to be used for each JSON object; defaults to Hash.
With the default, Hash:
source = '{"a": "foo", "b": 1.0, "c": true, "d": false, "e": null}' ruby = JSON.parse(source) ruby.class # => Hash
Use class OpenStruct:
ruby = JSON.parse(source, {object_class: OpenStruct}) ruby # => #<OpenStruct a="foo", b=1.0, c=true, d=false, e=nil>
Option array_class
(Class) specifies the Ruby class to be used for each JSON array; defaults to Array.
With the default, Array:
source = '["foo", 1.0, true, false, null]' ruby = JSON.parse(source) ruby.class # => Array
Use class Set:
ruby = JSON.parse(source, {array_class: Set}) ruby # => #<Set: {"foo", 1.0, true, false, nil}>
Option create_additions
(boolean) specifies whether to use JSON additions in parsing. See JSON Additions.
To generate a Ruby String containing JSON data, use method JSON.generate(source, opts)
, where
source
is a Ruby object.
opts
is a Hash object containing options that control both input allowed and output formatting.
When the source is a Ruby Array, JSON.generate
returns a String containing a JSON array:
ruby = [0, 's', :foo] json = JSON.generate(ruby) json # => '[0,"s","foo"]'
The Ruby Array array may contain nested arrays, hashes, and scalars to any depth:
ruby = [0, [1, 2], {foo: 3, bar: 4}] json = JSON.generate(ruby) json # => '[0,[1,2],{"foo":3,"bar":4}]'
When the source is a Ruby Hash, JSON.generate
returns a String containing a JSON object:
ruby = {foo: 0, bar: 's', baz: :bat} json = JSON.generate(ruby) json # => '{"foo":0,"bar":"s","baz":"bat"}'
The Ruby Hash array may contain nested arrays, hashes, and scalars to any depth:
ruby = {foo: [0, 1], bar: {baz: 2, bat: 3}, bam: :bad} json = JSON.generate(ruby) json # => '{"foo":[0,1],"bar":{"baz":2,"bat":3},"bam":"bad"}'
When the source is neither an Array nor a Hash, the generated JSON data depends on the class of the source.
When the source is a Ruby Integer or Float, JSON.generate
returns a String containing a JSON number:
JSON.generate(42) # => '42' JSON.generate(0.42) # => '0.42'
When the source is a Ruby String, JSON.generate
returns a String containing a JSON string (with double-quotes):
JSON.generate('A string') # => '"A string"'
When the source is true
, false
or nil
, JSON.generate
returns a String containing the corresponding JSON token:
JSON.generate(true) # => 'true' JSON.generate(false) # => 'false' JSON.generate(nil) # => 'null'
When the source is none of the above, JSON.generate
returns a String containing a JSON string representation of the source:
JSON.generate(:foo) # => '"foo"' JSON.generate(Complex(0, 0)) # => '"0+0i"' JSON.generate(Dir.new('.')) # => '"#<Dir>"'
Option allow_nan
(boolean) specifies whether NaN
, Infinity
, and -Infinity
may be generated; defaults to false
.
With the default, false
:
# Raises JSON::GeneratorError (920: NaN not allowed in JSON): JSON.generate(JSON::NaN) # Raises JSON::GeneratorError (917: Infinity not allowed in JSON): JSON.generate(JSON::Infinity) # Raises JSON::GeneratorError (917: -Infinity not allowed in JSON): JSON.generate(JSON::MinusInfinity)
Allow:
ruby = [Float::NaN, Float::Infinity, Float::MinusInfinity] JSON.generate(ruby, allow_nan: true) # => '[NaN,Infinity,-Infinity]'
Option max_nesting
(Integer) specifies the maximum nesting depth in obj
; defaults to 100
.
With the default, 100
:
obj = [[[[[[0]]]]]] JSON.generate(obj) # => '[[[[[[0]]]]]]'
Too deep:
# Raises JSON::NestingError (nesting of 2 is too deep): JSON.generate(obj, max_nesting: 2)
The default formatting options generate the most compact JSON data, all on one line and with no whitespace.
You can use these formatting options to generate JSON data in a more open format, using whitespace. See also JSON.pretty_generate
.
Option array_nl
(String) specifies a string (usually a newline) to be inserted after each JSON array; defaults to the empty String, ''
.
Option object_nl
(String) specifies a string (usually a newline) to be inserted after each JSON object; defaults to the empty String, ''
.
Option indent
(String) specifies the string (usually spaces) to be used for indentation; defaults to the empty String, ''
; defaults to the empty String, ''
; has no effect unless options array_nl
or object_nl
specify newlines.
Option space
(String) specifies a string (usually a space) to be inserted after the colon in each JSON object’s pair; defaults to the empty String, ''
.
Option space_before
(String) specifies a string (usually a space) to be inserted before the colon in each JSON object’s pair; defaults to the empty String, ''
.
In this example, obj
is used first to generate the shortest JSON data (no whitespace), then again with all formatting options specified:
obj = {foo: [:bar, :baz], bat: {bam: 0, bad: 1}} json = JSON.generate(obj) puts 'Compact:', json opts = { array_nl: "\n", object_nl: "\n", indent: ' ', space_before: ' ', space: ' ' } puts 'Open:', JSON.generate(obj, opts)
Output:
Compact: {"foo":["bar","baz"],"bat":{"bam":0,"bad":1}} Open: { "foo" : [ "bar", "baz" ], "bat" : { "bam" : 0, "bad" : 1 } }
When you “round trip” a non-String object from Ruby to JSON and back, you have a new String, instead of the object you began with:
ruby0 = Range.new(0, 2) json = JSON.generate(ruby0) json # => '0..2"' ruby1 = JSON.parse(json) ruby1 # => '0..2' ruby1.class # => String
You can use JSON additions to preserve the original object. The addition is an extension of a ruby class, so that:
JSON.generate stores more information in the JSON string.
JSON.parse, called with option create_additions
, uses that information to create a proper Ruby object.
This example shows a Range being generated into JSON and parsed back into Ruby, both without and with the addition for Range:
ruby = Range.new(0, 2) # This passage does not use the addition for Range. json0 = JSON.generate(ruby) ruby0 = JSON.parse(json0) # This passage uses the addition for Range. require 'json/add/range' json1 = JSON.generate(ruby) ruby1 = JSON.parse(json1, create_additions: true) # Make a nice display. display = <<EOT Generated JSON: Without addition: #{json0} (#{json0.class}) With addition: #{json1} (#{json1.class}) Parsed JSON: Without addition: #{ruby0.inspect} (#{ruby0.class}) With addition: #{ruby1.inspect} (#{ruby1.class}) EOT puts display
This output shows the different results:
Generated JSON: Without addition: "0..2" (String) With addition: {"json_class":"Range","a":[0,2,false]} (String) Parsed JSON: Without addition: "0..2" (String) With addition: 0..2 (Range)
The JSON module includes additions for certain classes. You can also craft custom additions. See Custom JSON Additions.
The JSON module includes additions for certain classes. To use an addition, require
its source:
BigDecimal: require 'json/add/bigdecimal'
Complex: require 'json/add/complex'
Date: require 'json/add/date'
DateTime: require 'json/add/date_time'
Exception: require 'json/add/exception'
OpenStruct: require 'json/add/ostruct'
Range: require 'json/add/range'
Rational: require 'json/add/rational'
Regexp: require 'json/add/regexp'
Set: require 'json/add/set'
Struct: require 'json/add/struct'
Symbol: require 'json/add/symbol'
Time: require 'json/add/time'
To reduce punctuation clutter, the examples below show the generated JSON via puts
, rather than the usual inspect
,
BigDecimal:
require 'json/add/bigdecimal' ruby0 = BigDecimal(0) # 0.0 json = JSON.generate(ruby0) # {"json_class":"BigDecimal","b":"27:0.0"} ruby1 = JSON.parse(json, create_additions: true) # 0.0 ruby1.class # => BigDecimal
Complex:
require 'json/add/complex' ruby0 = Complex(1+0i) # 1+0i json = JSON.generate(ruby0) # {"json_class":"Complex","r":1,"i":0} ruby1 = JSON.parse(json, create_additions: true) # 1+0i ruby1.class # Complex
Date:
require 'json/add/date' ruby0 = Date.today # 2020-05-02 json = JSON.generate(ruby0) # {"json_class":"Date","y":2020,"m":5,"d":2,"sg":2299161.0} ruby1 = JSON.parse(json, create_additions: true) # 2020-05-02 ruby1.class # Date
DateTime:
require 'json/add/date_time' ruby0 = DateTime.now # 2020-05-02T10:38:13-05:00 json = JSON.generate(ruby0) # {"json_class":"DateTime","y":2020,"m":5,"d":2,"H":10,"M":38,"S":13,"of":"-5/24","sg":2299161.0} ruby1 = JSON.parse(json, create_additions: true) # 2020-05-02T10:38:13-05:00 ruby1.class # DateTime
Exception (and its subclasses including RuntimeError):
require 'json/add/exception' ruby0 = Exception.new('A message') # A message json = JSON.generate(ruby0) # {"json_class":"Exception","m":"A message","b":null} ruby1 = JSON.parse(json, create_additions: true) # A message ruby1.class # Exception ruby0 = RuntimeError.new('Another message') # Another message json = JSON.generate(ruby0) # {"json_class":"RuntimeError","m":"Another message","b":null} ruby1 = JSON.parse(json, create_additions: true) # Another message ruby1.class # RuntimeError
OpenStruct:
require 'json/add/ostruct' ruby0 = OpenStruct.new(name: 'Matz', language: 'Ruby') # #<OpenStruct name="Matz", language="Ruby"> json = JSON.generate(ruby0) # {"json_class":"OpenStruct","t":{"name":"Matz","language":"Ruby"}} ruby1 = JSON.parse(json, create_additions: true) # #<OpenStruct name="Matz", language="Ruby"> ruby1.class # OpenStruct
Range:
require 'json/add/range' ruby0 = Range.new(0, 2) # 0..2 json = JSON.generate(ruby0) # {"json_class":"Range","a":[0,2,false]} ruby1 = JSON.parse(json, create_additions: true) # 0..2 ruby1.class # Range
Rational:
require 'json/add/rational' ruby0 = Rational(1, 3) # 1/3 json = JSON.generate(ruby0) # {"json_class":"Rational","n":1,"d":3} ruby1 = JSON.parse(json, create_additions: true) # 1/3 ruby1.class # Rational
Regexp:
require 'json/add/regexp' ruby0 = Regexp.new('foo') # (?-mix:foo) json = JSON.generate(ruby0) # {"json_class":"Regexp","o":0,"s":"foo"} ruby1 = JSON.parse(json, create_additions: true) # (?-mix:foo) ruby1.class # Regexp
Set:
require 'json/add/set' ruby0 = Set.new([0, 1, 2]) # #<Set: {0, 1, 2}> json = JSON.generate(ruby0) # {"json_class":"Set","a":[0,1,2]} ruby1 = JSON.parse(json, create_additions: true) # #<Set: {0, 1, 2}> ruby1.class # Set
Struct:
require 'json/add/struct' Customer = Struct.new(:name, :address) # Customer ruby0 = Customer.new("Dave", "123 Main") # #<struct Customer name="Dave", address="123 Main"> json = JSON.generate(ruby0) # {"json_class":"Customer","v":["Dave","123 Main"]} ruby1 = JSON.parse(json, create_additions: true) # #<struct Customer name="Dave", address="123 Main"> ruby1.class # Customer
Symbol:
require 'json/add/symbol' ruby0 = :foo # foo json = JSON.generate(ruby0) # {"json_class":"Symbol","s":"foo"} ruby1 = JSON.parse(json, create_additions: true) # foo ruby1.class # Symbol
Time:
require 'json/add/time' ruby0 = Time.now # 2020-05-02 11:28:26 -0500 json = JSON.generate(ruby0) # {"json_class":"Time","s":1588436906,"n":840560000} ruby1 = JSON.parse(json, create_additions: true) # 2020-05-02 11:28:26 -0500 ruby1.class # Time
In addition to the JSON additions provided, you can craft JSON additions of your own, either for Ruby built-in classes or for user-defined classes.
Here’s a user-defined class Foo
:
class Foo attr_accessor :bar, :baz def initialize(bar, baz) self.bar = bar self.baz = baz end end
Here’s the JSON addition for it:
# Extend class Foo with JSON addition. class Foo # Serialize Foo object with its class name and arguments def to_json(*args) { JSON.create_id => self.class.name, 'a' => [ bar, baz ] }.to_json(*args) end # Deserialize JSON string by constructing new Foo object with arguments. def self.json_create(object) new(*object['a']) end end
Demonstration:
require 'json' # This Foo object has no custom addition. foo0 = Foo.new(0, 1) json0 = JSON.generate(foo0) obj0 = JSON.parse(json0) # Lood the custom addition. require_relative 'foo_addition' # This foo has the custom addition. foo1 = Foo.new(0, 1) json1 = JSON.generate(foo1) obj1 = JSON.parse(json1, create_additions: true) # Make a nice display. display = <<EOT Generated JSON: Without custom addition: #{json0} (#{json0.class}) With custom addition: #{json1} (#{json1.class}) Parsed JSON: Without custom addition: #{obj0.inspect} (#{obj0.class}) With custom addition: #{obj1.inspect} (#{obj1.class}) EOT puts display
Output:
Generated JSON: Without custom addition: "#<Foo:0x0000000006534e80>" (String) With custom addition: {"json_class":"Foo","a":[0,1]} (String) Parsed JSON: Without custom addition: "#<Foo:0x0000000006534e80>" (String) With custom addition: #<Foo:0x0000000006473bb8 @bar=0, @baz=1> (Foo)
Kanji Converter for Ruby.
The objspace library extends the ObjectSpace
module and adds several methods to get internal statistic information about object/memory management.
You need to require 'objspace'
to use this extension module.
Generally, you *SHOULD NOT* use this library if you do not know about the MRI implementation. Mainly, this library is for (memory) profiler developers and MRI developers who need to know about MRI memory usage.
The ObjectSpace
module contains a number of routines that interact with the garbage collection facility and allow you to traverse all living objects with an iterator.
ObjectSpace
also provides support for object finalizers, procs that will be called when a specific object is about to be destroyed by garbage collection. See the documentation for ObjectSpace.define_finalizer
for important information on how to use this method correctly.
a = "A" b = "B" ObjectSpace.define_finalizer(a, proc {|id| puts "Finalizer one on #{id}" }) ObjectSpace.define_finalizer(b, proc {|id| puts "Finalizer two on #{id}" }) a = nil b = nil
produces:
Finalizer two on 537763470 Finalizer one on 537763480
OpenSSL
provides SSL
, TLS and general purpose cryptography. It wraps the OpenSSL library.
All examples assume you have loaded OpenSSL
with:
require 'openssl'
These examples build atop each other. For example the key created in the next is used in throughout these examples.
This example creates a 2048 bit RSA keypair and writes it to the current directory.
key = OpenSSL::PKey::RSA.new 2048 open 'private_key.pem', 'w' do |io| io.write key.to_pem end open 'public_key.pem', 'w' do |io| io.write key.public_key.to_pem end
Keys saved to disk without encryption are not secure as anyone who gets ahold of the key may use it unless it is encrypted. In order to securely export a key you may export it with a pass phrase.
cipher = OpenSSL::Cipher.new 'aes-256-cbc' pass_phrase = 'my secure pass phrase goes here' key_secure = key.export cipher, pass_phrase open 'private.secure.pem', 'w' do |io| io.write key_secure end
OpenSSL::Cipher.ciphers
returns a list of available ciphers.
A key can also be loaded from a file.
key2 = OpenSSL::PKey.read File.read 'private_key.pem' key2.public? # => true key2.private? # => true
or
key3 = OpenSSL::PKey.read File.read 'public_key.pem' key3.public? # => true key3.private? # => false
OpenSSL
will prompt you for your pass phrase when loading an encrypted key. If you will not be able to type in the pass phrase you may provide it when loading the key:
key4_pem = File.read 'private.secure.pem' pass_phrase = 'my secure pass phrase goes here' key4 = OpenSSL::PKey.read key4_pem, pass_phrase
RSA provides encryption and decryption using the public and private keys. You can use a variety of padding methods depending upon the intended use of encrypted data.
Asymmetric public/private key encryption is slow and victim to attack in cases where it is used without padding or directly to encrypt larger chunks of data. Typical use cases for RSA encryption involve “wrapping” a symmetric key with the public key of the recipient who would “unwrap” that symmetric key again using their private key. The following illustrates a simplified example of such a key transport scheme. It shouldn’t be used in practice, though, standardized protocols should always be preferred.
wrapped_key = key.public_encrypt key
A symmetric key encrypted with the public key can only be decrypted with the corresponding private key of the recipient.
original_key = key.private_decrypt wrapped_key
By default PKCS#1 padding will be used, but it is also possible to use other forms of padding, see PKey::RSA
for further details.
Using “private_encrypt” to encrypt some data with the private key is equivalent to applying a digital signature to the data. A verifying party may validate the signature by comparing the result of decrypting the signature with “public_decrypt” to the original data. However, OpenSSL::PKey
already has methods “sign” and “verify” that handle digital signatures in a standardized way - “private_encrypt” and “public_decrypt” shouldn’t be used in practice.
To sign a document, a cryptographically secure hash of the document is computed first, which is then signed using the private key.
signature = key.sign 'SHA256', document
To validate the signature, again a hash of the document is computed and the signature is decrypted using the public key. The result is then compared to the hash just computed, if they are equal the signature was valid.
if key.verify 'SHA256', signature, document puts 'Valid' else puts 'Invalid' end
If supported by the underlying OpenSSL
version used, Password-based Encryption should use the features of PKCS5
. If not supported or if required by legacy applications, the older, less secure methods specified in RFC 2898 are also supported (see below).
PKCS5
supports PBKDF2 as it was specified in PKCS#5 v2.0. It still uses a password, a salt, and additionally a number of iterations that will slow the key derivation process down. The slower this is, the more work it requires being able to brute-force the resulting key.
The strategy is to first instantiate a Cipher
for encryption, and then to generate a random IV plus a key derived from the password using PBKDF2. PKCS #5 v2.0 recommends at least 8 bytes for the salt, the number of iterations largely depends on the hardware being used.
cipher = OpenSSL::Cipher.new 'aes-256-cbc' cipher.encrypt iv = cipher.random_iv pwd = 'some hopefully not to easily guessable password' salt = OpenSSL::Random.random_bytes 16 iter = 20000 key_len = cipher.key_len digest = OpenSSL::Digest.new('SHA256') key = OpenSSL::PKCS5.pbkdf2_hmac(pwd, salt, iter, key_len, digest) cipher.key = key Now encrypt the data: encrypted = cipher.update document encrypted << cipher.final
Use the same steps as before to derive the symmetric AES key, this time setting the Cipher
up for decryption.
cipher = OpenSSL::Cipher.new 'aes-256-cbc' cipher.decrypt cipher.iv = iv # the one generated with #random_iv pwd = 'some hopefully not to easily guessable password' salt = ... # the one generated above iter = 20000 key_len = cipher.key_len digest = OpenSSL::Digest.new('SHA256') key = OpenSSL::PKCS5.pbkdf2_hmac(pwd, salt, iter, key_len, digest) cipher.key = key Now decrypt the data: decrypted = cipher.update encrypted decrypted << cipher.final
PKCS #5 is a password-based encryption standard documented at RFC2898. It allows a short password or passphrase to be used to create a secure encryption key. If possible, PBKDF2 as described above should be used if the circumstances allow it.
PKCS #5 uses a Cipher
, a pass phrase and a salt to generate an encryption key.
pass_phrase = 'my secure pass phrase goes here' salt = '8 octets'
First set up the cipher for encryption
encryptor = OpenSSL::Cipher.new 'aes-256-cbc' encryptor.encrypt encryptor.pkcs5_keyivgen pass_phrase, salt
Then pass the data you want to encrypt through
encrypted = encryptor.update 'top secret document' encrypted << encryptor.final
Use a new Cipher
instance set up for decryption
decryptor = OpenSSL::Cipher.new 'aes-256-cbc' decryptor.decrypt decryptor.pkcs5_keyivgen pass_phrase, salt
Then pass the data you want to decrypt through
plain = decryptor.update encrypted plain << decryptor.final
X509
Certificates This example creates a self-signed certificate using an RSA key and a SHA1 signature.
key = OpenSSL::PKey::RSA.new 2048 name = OpenSSL::X509::Name.parse '/CN=nobody/DC=example' cert = OpenSSL::X509::Certificate.new cert.version = 2 cert.serial = 0 cert.not_before = Time.now cert.not_after = Time.now + 3600 cert.public_key = key.public_key cert.subject = name
You can add extensions to the certificate with OpenSSL::SSL::ExtensionFactory to indicate the purpose of the certificate.
extension_factory = OpenSSL::X509::ExtensionFactory.new nil, cert cert.add_extension \ extension_factory.create_extension('basicConstraints', 'CA:FALSE', true) cert.add_extension \ extension_factory.create_extension( 'keyUsage', 'keyEncipherment,dataEncipherment,digitalSignature') cert.add_extension \ extension_factory.create_extension('subjectKeyIdentifier', 'hash')
The list of supported extensions (and in some cases their possible values) can be derived from the “objects.h” file in the OpenSSL
source code.
To sign a certificate set the issuer and use OpenSSL::X509::Certificate#sign
with a digest algorithm. This creates a self-signed cert because we’re using the same name and key to sign the certificate as was used to create the certificate.
cert.issuer = name cert.sign key, OpenSSL::Digest.new('SHA1') open 'certificate.pem', 'w' do |io| io.write cert.to_pem end
Like a key, a cert can also be loaded from a file.
cert2 = OpenSSL::X509::Certificate.new File.read 'certificate.pem'
Certificate#verify will return true when a certificate was signed with the given public key.
raise 'certificate can not be verified' unless cert2.verify key
A certificate authority (CA) is a trusted third party that allows you to verify the ownership of unknown certificates. The CA issues key signatures that indicate it trusts the user of that key. A user encountering the key can verify the signature by using the CA’s public key.
CA keys are valuable, so we encrypt and save it to disk and make sure it is not readable by other users.
ca_key = OpenSSL::PKey::RSA.new 2048 pass_phrase = 'my secure pass phrase goes here' cipher = OpenSSL::Cipher.new 'aes-256-cbc' open 'ca_key.pem', 'w', 0400 do |io| io.write ca_key.export(cipher, pass_phrase) end
A CA certificate is created the same way we created a certificate above, but with different extensions.
ca_name = OpenSSL::X509::Name.parse '/CN=ca/DC=example' ca_cert = OpenSSL::X509::Certificate.new ca_cert.serial = 0 ca_cert.version = 2 ca_cert.not_before = Time.now ca_cert.not_after = Time.now + 86400 ca_cert.public_key = ca_key.public_key ca_cert.subject = ca_name ca_cert.issuer = ca_name extension_factory = OpenSSL::X509::ExtensionFactory.new extension_factory.subject_certificate = ca_cert extension_factory.issuer_certificate = ca_cert ca_cert.add_extension \ extension_factory.create_extension('subjectKeyIdentifier', 'hash')
This extension indicates the CA’s key may be used as a CA.
ca_cert.add_extension \ extension_factory.create_extension('basicConstraints', 'CA:TRUE', true)
This extension indicates the CA’s key may be used to verify signatures on both certificates and certificate revocations.
ca_cert.add_extension \ extension_factory.create_extension( 'keyUsage', 'cRLSign,keyCertSign', true)
Root CA certificates are self-signed.
ca_cert.sign ca_key, OpenSSL::Digest.new('SHA1')
The CA certificate is saved to disk so it may be distributed to all the users of the keys this CA will sign.
open 'ca_cert.pem', 'w' do |io| io.write ca_cert.to_pem end
The CA signs keys through a Certificate Signing Request (CSR). The CSR contains the information necessary to identify the key.
csr = OpenSSL::X509::Request.new csr.version = 0 csr.subject = name csr.public_key = key.public_key csr.sign key, OpenSSL::Digest.new('SHA1')
A CSR is saved to disk and sent to the CA for signing.
open 'csr.pem', 'w' do |io| io.write csr.to_pem end
Upon receiving a CSR the CA will verify it before signing it. A minimal verification would be to check the CSR’s signature.
csr = OpenSSL::X509::Request.new File.read 'csr.pem' raise 'CSR can not be verified' unless csr.verify csr.public_key
After verification a certificate is created, marked for various usages, signed with the CA key and returned to the requester.
csr_cert = OpenSSL::X509::Certificate.new csr_cert.serial = 0 csr_cert.version = 2 csr_cert.not_before = Time.now csr_cert.not_after = Time.now + 600 csr_cert.subject = csr.subject csr_cert.public_key = csr.public_key csr_cert.issuer = ca_cert.subject extension_factory = OpenSSL::X509::ExtensionFactory.new extension_factory.subject_certificate = csr_cert extension_factory.issuer_certificate = ca_cert csr_cert.add_extension \ extension_factory.create_extension('basicConstraints', 'CA:FALSE') csr_cert.add_extension \ extension_factory.create_extension( 'keyUsage', 'keyEncipherment,dataEncipherment,digitalSignature') csr_cert.add_extension \ extension_factory.create_extension('subjectKeyIdentifier', 'hash') csr_cert.sign ca_key, OpenSSL::Digest.new('SHA1') open 'csr_cert.pem', 'w' do |io| io.write csr_cert.to_pem end
SSL
and TLS Connections Using our created key and certificate we can create an SSL
or TLS connection. An SSLContext is used to set up an SSL
session.
context = OpenSSL::SSL::SSLContext.new
SSL
Server An SSL
server requires the certificate and private key to communicate securely with its clients:
context.cert = cert context.key = key
Then create an SSLServer with a TCP server socket and the context. Use the SSLServer like an ordinary TCP server.
require 'socket' tcp_server = TCPServer.new 5000 ssl_server = OpenSSL::SSL::SSLServer.new tcp_server, context loop do ssl_connection = ssl_server.accept data = ssl_connection.gets response = "I got #{data.dump}" puts response ssl_connection.puts "I got #{data.dump}" ssl_connection.close end
SSL
client An SSL
client is created with a TCP socket and the context. SSLSocket#connect must be called to initiate the SSL
handshake and start encryption. A key and certificate are not required for the client socket.
Note that SSLSocket#close doesn’t close the underlying socket by default. Set
SSLSocket#sync_close to true if you want.
require 'socket' tcp_socket = TCPSocket.new 'localhost', 5000 ssl_client = OpenSSL::SSL::SSLSocket.new tcp_socket, context ssl_client.sync_close = true ssl_client.connect ssl_client.puts "hello server!" puts ssl_client.gets ssl_client.close # shutdown the TLS connection and close tcp_socket
An unverified SSL
connection does not provide much security. For enhanced security the client or server can verify the certificate of its peer.
The client can be modified to verify the server’s certificate against the certificate authority’s certificate:
context.ca_file = 'ca_cert.pem' context.verify_mode = OpenSSL::SSL::VERIFY_PEER require 'socket' tcp_socket = TCPSocket.new 'localhost', 5000 ssl_client = OpenSSL::SSL::SSLSocket.new tcp_socket, context ssl_client.connect ssl_client.puts "hello server!" puts ssl_client.gets
If the server certificate is invalid or context.ca_file
is not set when verifying peers an OpenSSL::SSL::SSLError
will be raised.
Creates and manages pseudo terminals (PTYs). See also en.wikipedia.org/wiki/Pseudo_terminal
PTY
allows you to allocate new terminals using ::open
or ::spawn
a new terminal with a specific command.
In this example we will change the buffering type in the factor
command, assuming that factor uses stdio for stdout buffering.
If IO.pipe
is used instead of PTY.open
, this code deadlocks because factor’s stdout is fully buffered.
# start by requiring the standard library PTY require 'pty' master, slave = PTY.open read, write = IO.pipe pid = spawn("factor", :in=>read, :out=>slave) read.close # we dont need the read slave.close # or the slave # pipe "42" to the factor command write.puts "42" # output the response from factor p master.gets #=> "42: 2 3 7\n" # pipe "144" to factor and print out the response write.puts "144" p master.gets #=> "144: 2 2 2 2 3 3\n" write.close # close the pipe # The result of read operation when pty slave is closed is platform # dependent. ret = begin master.gets # FreeBSD returns nil. rescue Errno::EIO # GNU/Linux raises EIO. nil end p ret #=> nil
© Copyright 1998 by Akinori Ito.
This software may be redistributed freely for this purpose, in full or in part, provided that this entire copyright notice is included on any copies of this software and applications and derivations thereof.
This software is provided on an “as is” basis, without warranty of any kind, either expressed or implied, as to any matter including, but not limited to warranty of fitness of purpose, or merchantability, or results obtained from use of this software.
The Base64
module provides for the encoding (encode64
, strict_encode64
, urlsafe_encode64
) and decoding (decode64
, strict_decode64
, urlsafe_decode64
) of binary data using a Base64
representation.
A simple encoding and decoding.
require "base64" enc = Base64.encode64('Send reinforcements') # -> "U2VuZCByZWluZm9yY2VtZW50cw==\n" plain = Base64.decode64(enc) # -> "Send reinforcements"
The purpose of using base64 to encode data is that it translates any binary data into purely printable characters.
The Benchmark
module provides methods to measure and report the time used to execute Ruby code.
Measure the time to construct the string given by the expression "a"*1_000_000_000
:
require 'benchmark' puts Benchmark.measure { "a"*1_000_000_000 }
On my machine (OSX 10.8.3 on i5 1.7 GHz) this generates:
0.350000 0.400000 0.750000 ( 0.835234)
This report shows the user CPU time, system CPU time, the sum of the user and system CPU times, and the elapsed real time. The unit of time is seconds.
Do some experiments sequentially using the bm
method:
require 'benchmark' n = 5000000 Benchmark.bm do |x| x.report { for i in 1..n; a = "1"; end } x.report { n.times do ; a = "1"; end } x.report { 1.upto(n) do ; a = "1"; end } end
The result:
user system total real 1.010000 0.000000 1.010000 ( 1.014479) 1.000000 0.000000 1.000000 ( 0.998261) 0.980000 0.000000 0.980000 ( 0.981335)
Continuing the previous example, put a label in each report:
require 'benchmark' n = 5000000 Benchmark.bm(7) do |x| x.report("for:") { for i in 1..n; a = "1"; end } x.report("times:") { n.times do ; a = "1"; end } x.report("upto:") { 1.upto(n) do ; a = "1"; end } end
The result:
user system total real for: 1.010000 0.000000 1.010000 ( 1.015688) times: 1.000000 0.000000 1.000000 ( 1.003611) upto: 1.030000 0.000000 1.030000 ( 1.028098)
The times for some benchmarks depend on the order in which items are run. These differences are due to the cost of memory allocation and garbage collection. To avoid these discrepancies, the bmbm
method is provided. For example, to compare ways to sort an array of floats:
require 'benchmark' array = (1..1000000).map { rand } Benchmark.bmbm do |x| x.report("sort!") { array.dup.sort! } x.report("sort") { array.dup.sort } end
The result:
Rehearsal ----------------------------------------- sort! 1.490000 0.010000 1.500000 ( 1.490520) sort 1.460000 0.000000 1.460000 ( 1.463025) -------------------------------- total: 2.960000sec user system total real sort! 1.460000 0.000000 1.460000 ( 1.460465) sort 1.450000 0.010000 1.460000 ( 1.448327)
Report statistics of sequential experiments with unique labels, using the benchmark
method:
require 'benchmark' include Benchmark # we need the CAPTION and FORMAT constants n = 5000000 Benchmark.benchmark(CAPTION, 7, FORMAT, ">total:", ">avg:") do |x| tf = x.report("for:") { for i in 1..n; a = "1"; end } tt = x.report("times:") { n.times do ; a = "1"; end } tu = x.report("upto:") { 1.upto(n) do ; a = "1"; end } [tf+tt+tu, (tf+tt+tu)/3] end
The result:
user system total real for: 0.950000 0.000000 0.950000 ( 0.952039) times: 0.980000 0.000000 0.980000 ( 0.984938) upto: 0.950000 0.000000 0.950000 ( 0.946787) >total: 2.880000 0.000000 2.880000 ( 2.883764) >avg: 0.960000 0.000000 0.960000 ( 0.961255)
Copyright © 2000-2007 Minero Aoki
This program is free software. You can distribute/modify this program under the same terms of ruby.
FileUtils
Namespace for several file utility methods for copying, moving, removing, etc.
Module
Functions require 'fileutils' FileUtils.cd(dir, **options) FileUtils.cd(dir, **options) {|dir| block } FileUtils.pwd() FileUtils.mkdir(dir, **options) FileUtils.mkdir(list, **options) FileUtils.mkdir_p(dir, **options) FileUtils.mkdir_p(list, **options) FileUtils.rmdir(dir, **options) FileUtils.rmdir(list, **options) FileUtils.ln(target, link, **options) FileUtils.ln(targets, dir, **options) FileUtils.ln_s(target, link, **options) FileUtils.ln_s(targets, dir, **options) FileUtils.ln_sf(target, link, **options) FileUtils.cp(src, dest, **options) FileUtils.cp(list, dir, **options) FileUtils.cp_r(src, dest, **options) FileUtils.cp_r(list, dir, **options) FileUtils.mv(src, dest, **options) FileUtils.mv(list, dir, **options) FileUtils.rm(list, **options) FileUtils.rm_r(list, **options) FileUtils.rm_rf(list, **options) FileUtils.install(src, dest, **options) FileUtils.chmod(mode, list, **options) FileUtils.chmod_R(mode, list, **options) FileUtils.chown(user, group, list, **options) FileUtils.chown_R(user, group, list, **options) FileUtils.touch(list, **options)
Possible options
are:
:force
forced operation (rewrite files if exist, remove directories if not empty, etc.);
:verbose
print command to be run, in bash syntax, before performing it;
:preserve
preserve object’s group, user and modification time on copying;
:noop
no changes are made (usable in combination with :verbose
which will print the command to run)
Each method documents the options that it honours. See also ::commands
, ::options
and ::options_of
methods to introspect which command have which options.
All methods that have the concept of a “source” file or directory can take either one file or a list of files in that argument. See the method documentation for examples.
There are some ‘low level’ methods, which do not accept keyword arguments:
FileUtils.copy_entry(src, dest, preserve = false, dereference_root = false, remove_destination = false) FileUtils.copy_file(src, dest, preserve = false, dereference = true) FileUtils.copy_stream(srcstream, deststream) FileUtils.remove_entry(path, force = false) FileUtils.remove_entry_secure(path, force = false) FileUtils.remove_file(path, force = false) FileUtils.compare_file(path_a, path_b) FileUtils.compare_stream(stream_a, stream_b) FileUtils.uptodate?(file, cmp_list)
FileUtils::Verbose
This module has all methods of FileUtils
module, but it outputs messages before acting. This equates to passing the :verbose
flag to methods in FileUtils
.
FileUtils::NoWrite
This module has all methods of FileUtils
module, but never changes files/directories. This equates to passing the :noop
flag to methods in FileUtils
.
FileUtils::DryRun
This module has all methods of FileUtils
module, but never changes files/directories. This equates to passing the :noop
and :verbose
flags to methods in FileUtils
.
The Forwardable module provides delegation of specified methods to a designated object, using the methods def_delegator
and def_delegators
.
For example, say you have a class RecordCollection which contains an array @records
. You could provide the lookup method record_number(), which simply calls [] on the @records
array, like this:
require 'forwardable' class RecordCollection attr_accessor :records extend Forwardable def_delegator :@records, :[], :record_number end
We can use the lookup method like so:
r = RecordCollection.new r.records = [4,5,6] r.record_number(0) # => 4
Further, if you wish to provide the methods size, <<, and map, all of which delegate to @records, this is how you can do it:
class RecordCollection # re-open RecordCollection class def_delegators :@records, :size, :<<, :map end r = RecordCollection.new r.records = [1,2,3] r.record_number(0) # => 1 r.size # => 3 r << 4 # => [1, 2, 3, 4] r.map { |x| x * 2 } # => [2, 4, 6, 8]
You can even extend regular objects with Forwardable.
my_hash = Hash.new my_hash.extend Forwardable # prepare object for delegation my_hash.def_delegator "STDOUT", "puts" # add delegation for STDOUT.puts() my_hash.puts "Howdy!"
You could use Forwardable as an alternative to inheritance, when you don’t want to inherit all methods from the superclass. For instance, here is how you might add a range of Array
instance methods to a new class Queue
:
class Queue extend Forwardable def initialize @q = [ ] # prepare delegate object end # setup preferred interface, enq() and deq()... def_delegator :@q, :push, :enq def_delegator :@q, :shift, :deq # support some general Array methods that fit Queues well def_delegators :@q, :clear, :first, :push, :shift, :size end q = Thread::Queue.new q.enq 1, 2, 3, 4, 5 q.push 6 q.shift # => 1 while q.size > 0 puts q.deq end q.enq "Ruby", "Perl", "Python" puts q.first q.clear puts q.first
This should output:
2 3 4 5 6 Ruby nil
Be advised, RDoc
will not detect delegated methods.
forwardable.rb
provides single-method delegation via the def_delegator
and def_delegators
methods. For full-class delegation via DelegateClass, see delegate.rb
.
SingleForwardable
can be used to setup delegation at the object level as well.
printer = String.new printer.extend SingleForwardable # prepare object for delegation printer.def_delegator "STDOUT", "puts" # add delegation for STDOUT.puts() printer.puts "Howdy!"
Also, SingleForwardable
can be used to set up delegation for a Class
or Module
.
class Implementation def self.service puts "serviced!" end end module Facade extend SingleForwardable def_delegator :Implementation, :service end Facade.service #=> serviced!
If you want to use both Forwardable and SingleForwardable
, you can use methods def_instance_delegator and def_single_delegator
, etc.
OpenURI
is an easy-to-use wrapper for Net::HTTP
, Net::HTTPS and Net::FTP.
It is possible to open an http, https or ftp URL as though it were a file:
URI.open("http://www.ruby-lang.org/") {|f| f.each_line {|line| p line} }
The opened file has several getter methods for its meta-information, as follows, since it is extended by OpenURI::Meta
.
URI.open("http://www.ruby-lang.org/en") {|f| f.each_line {|line| p line} p f.base_uri # <URI::HTTP:0x40e6ef2 URL:http://www.ruby-lang.org/en/> p f.content_type # "text/html" p f.charset # "iso-8859-1" p f.content_encoding # [] p f.last_modified # Thu Dec 05 02:45:02 UTC 2002 }
Additional header fields can be specified by an optional hash argument.
URI.open("http://www.ruby-lang.org/en/", "User-Agent" => "Ruby/#{RUBY_VERSION}", "From" => "foo@bar.invalid", "Referer" => "http://www.ruby-lang.org/") {|f| # ... }
The environment variables such as http_proxy, https_proxy and ftp_proxy are in effect by default. Here we disable proxy:
URI.open("http://www.ruby-lang.org/en/", :proxy => nil) {|f| # ... }
See OpenURI::OpenRead.open
and URI.open
for more on available options.
URI
objects can be opened in a similar way.
uri = URI.parse("http://www.ruby-lang.org/en/") uri.open {|f| # ... }
URI
objects can be read directly. The returned string is also extended by OpenURI::Meta
.
str = uri.read p str.base_uri
Tanaka Akira <akr@m17n.org>
The Singleton
module implements the Singleton
pattern.
To use Singleton
, include the module in your class.
class Klass include Singleton # ... end
This ensures that only one instance of Klass can be created.
a,b = Klass.instance, Klass.instance a == b # => true Klass.new # => NoMethodError - new is private ...
The instance is created at upon the first call of Klass.instance().
class OtherKlass include Singleton # ... end ObjectSpace.each_object(OtherKlass){} # => 0 OtherKlass.instance ObjectSpace.each_object(OtherKlass){} # => 1
This behavior is preserved under inheritance and cloning.
This above is achieved by:
Making Klass.new and Klass.allocate private.
Overriding Klass.inherited(sub_klass) and Klass.clone() to ensure that the Singleton
properties are kept when inherited and cloned.
Providing the Klass.instance() method that returns the same object each time it is called.
Overriding Klass._load(str) to call Klass.instance().
Overriding Klass#clone and Klass#dup to raise TypeErrors to prevent cloning or duping.
Singleton
and Marshal
By default Singleton’s _dump(depth)
returns the empty string. Marshalling by default will strip state information, e.g. instance variables from the instance. Classes using Singleton
can provide custom _load(str) and _dump(depth) methods to retain some of the previous state of the instance.
require 'singleton' class Example include Singleton attr_accessor :keep, :strip def _dump(depth) # this strips the @strip information from the instance Marshal.dump(@keep, depth) end def self._load(str) instance.keep = Marshal.load(str) instance end end a = Example.instance a.keep = "keep this" a.strip = "get rid of this" stored_state = Marshal.dump(a) a.keep = nil a.strip = nil b = Marshal.load(stored_state) p a == b # => true p a.keep # => "keep this" p a.strip # => nil
Timeout
long-running blocks
require 'timeout' status = Timeout::timeout(5) { # Something that should be interrupted if it takes more than 5 seconds... }
Timeout
provides a way to auto-terminate a potentially long-running operation if it hasn’t finished in a fixed amount of time.
Previous versions didn’t use a module for namespacing, however timeout
is provided for backwards compatibility. You should prefer Timeout.timeout
instead.
© 2000 Network Applied Communication Laboratory, Inc.
© 2000 Information-technology Promotion Agency, Japan
This is not an existing class, but documentation of the interface that Scheduler object should comply to in order to be used as argument to Fiber.scheduler
and handle non-blocking fibers. See also the “Non-blocking fibers” section in Fiber
class docs for explanations of some concepts.
Scheduler’s behavior and usage are expected to be as follows:
When the execution in the non-blocking Fiber
reaches some blocking operation (like sleep, wait for a process, or a non-ready I/O), it calls some of the scheduler’s hook methods, listed below.
Scheduler somehow registers what the current fiber is waiting on, and yields control to other fibers with Fiber.yield
(so the fiber would be suspended while expecting its wait to end, and other fibers in the same thread can perform)
At the end of the current thread execution, the scheduler’s method close
is called
The scheduler runs into a wait loop, checking all the blocked fibers (which it has registered on hook calls) and resuming them when the awaited resource is ready (e.g. I/O ready or sleep time elapsed).
A typical implementation would probably rely for this closing loop on a gem like EventMachine or Async.
This way concurrent execution will be achieved transparently for every individual Fiber’s code.
Hook methods are:
(the list is expanded as Ruby developers make more methods having non-blocking calls)
When not specified otherwise, the hook implementations are mandatory: if they are not implemented, the methods trying to call hook will fail. To provide backward compatibility, in the future hooks will be optional (if they are not implemented, due to the scheduler being created for the older Ruby version, the code which needs this hook will not fail, and will just behave in a blocking fashion).
It is also strongly recommended that the scheduler implements the fiber
method, which is delegated to by Fiber.schedule
.
Sample toy implementation of the scheduler can be found in Ruby’s code, in test/fiber/scheduler.rb
A base class for objects representing a C union
The base exception for JSON
errors.
This exception is raised if the nesting of parsed data structures is too deep.
This exception is raised if a generator or unparser error occurs.
This class is used as a return value from ObjectSpace::reachable_objects_from
.
When ObjectSpace::reachable_objects_from
returns an object with references to an internal object, an instance of this class is returned.
You can use the type
method to check the type of the internal object.
Generic error, common for all classes under OpenSSL
module
General error for openssl library configuration files. Including formatting, parsing errors, etc.