Raised when Ruby
can’t yield as requested.
A typical scenario is attempting to yield when no block is given:
def call_block yield 42 end call_block
raises the exception:
LocalJumpError: no block given (yield)
A more subtle example:
def get_me_a_return Proc.new { return 42 } end get_me_a_return.call
raises the exception:
LocalJumpError: unexpected return
Raised in case of a stack overflow.
def me_myself_and_i me_myself_and_i end me_myself_and_i
raises the exception:
SystemStackError: stack level too deep
Raised when given an invalid regexp expression.
Regexp.new("?")
raises the exception:
RegexpError: target of repeat operator is not specified: /?/
Raised when an invalid operation is attempted on a thread.
For example, when no other thread has been started:
Thread.stop
This will raises the following exception:
ThreadError: stopping only thread note: use sleep to stop forever
Raised when throw
is called with a tag which does not have corresponding catch
block.
throw "foo", "bar"
raises the exception:
UncaughtThrowError: uncaught throw "foo"
The Kernel
module is included by class Object
, so its methods are available in every Ruby
object.
The Kernel
instance methods are documented in class Object
while the module methods are documented here. These methods are called without a receiver and thus can be called in functional form:
sprintf "%.1f", 1.234 #=> "1.2"
Module
Kernel provides methods that are useful for:
__callee__
: Returns the called name of the current method as a symbol.
__dir__
: Returns the path to the directory from which the current method is called.
__method__
: Returns the name of the current method as a symbol.
autoload?
: Returns the file to be loaded when the given module is referenced.
binding
: Returns a Binding
for the context at the point of call.
block_given?
: Returns true
if a block was passed to the calling method.
caller
: Returns the current execution stack as an array of strings.
caller_locations
: Returns the current execution stack as an array of Thread::Backtrace::Location
objects.
class
: Returns the class of self
.
frozen?
: Returns whether self
is frozen.
global_variables
: Returns an array of global variables as symbols.
local_variables
: Returns an array of local variables as symbols.
test
: Performs specified tests on the given single file or pair of files.
abort
: Exits the current process after printing the given arguments.
at_exit
: Executes the given block when the process exits.
exit
: Exits the current process after calling any registered at_exit
handlers.
exit!
: Exits the current process without calling any registered at_exit
handlers.
catch
: Executes the given block, possibly catching a thrown object.
raise
(aliased as fail
): Raises an exception based on the given arguments.
throw
: Returns from the active catch block waiting for the given tag.
::pp
: Prints the given objects in pretty form.
gets
: Returns and assigns to $_
the next line from the current input.
open
: Creates an IO
object connected to the given stream, file, or subprocess.
p
: Prints the given objects’ inspect output to the standard output.
print
: Prints the given objects to standard output without a newline.
printf
: Prints the string resulting from applying the given format string to any additional arguments.
putc
: Equivalent to $stdout.putc(object)
for the given object.
puts
: Equivalent to $stdout.puts(*objects)
for the given objects.
readline
: Similar to gets
, but raises an exception at the end of file.
readlines
: Returns an array of the remaining lines from the current input.
lambda
: Returns a lambda proc for the given block.
set_trace_func
: Sets the given proc as the handler for tracing, or disables tracing if given nil
.
trace_var
: Starts tracing assignments to the given global variable.
untrace_var
: Disables tracing of assignments to the given global variable.
`command`: Returns the standard output of running command
in a subshell.
exec
: Replaces current process with a new process.
fork
: Forks the current process into two processes.
spawn
: Executes the given command and returns its pid without waiting for completion.
system
: Executes the given command in a subshell.
autoload
: Registers the given file to be loaded when the given constant is first referenced.
require
: Loads the given Ruby
file unless it has already been loaded.
require_relative
: Loads the Ruby
file path relative to the calling file, unless it has already been loaded.
tap
: Yields self
to the given block; returns self
.
then
(aliased as yield_self
): Yields self
to the block and returns the result of the block.
rand
: Returns a pseudo-random floating point number strictly between 0.0 and 1.0.
srand
: Seeds the pseudo-random number generator with the given number.
loop
: Repeatedly executes the given block.
sleep
: Suspends the current thread for the given number of seconds.
sprintf
(aliased as format
): Returns the string resulting from applying the given format string to any additional arguments.
syscall
: Runs an operating system call.
trap
: Specifies the handling of system signals.
warn
: Issue a warning based on the given messages and options.
Module
Enumerable provides methods that are useful to a collection class for:
These methods return information about the Enumerable other than the elements themselves:
member?
(aliased as include?
): Returns true
if self == object
, false
otherwise.
all?
: Returns true
if all elements meet a specified criterion; false
otherwise.
any?
: Returns true
if any element meets a specified criterion; false
otherwise.
none?
: Returns true
if no element meets a specified criterion; false
otherwise.
one?
: Returns true
if exactly one element meets a specified criterion; false
otherwise.
count
: Returns the count of elements, based on an argument or block criterion, if given.
tally
: Returns a new Hash
containing the counts of occurrences of each element.
These methods return entries from the Enumerable, without modifying it:
Leading, trailing, or all elements:
first
: Returns the first element or leading elements.
take
: Returns a specified number of leading elements.
drop
: Returns a specified number of trailing elements.
take_while
: Returns leading elements as specified by the given block.
drop_while
: Returns trailing elements as specified by the given block.
Minimum and maximum value elements:
min
: Returns the elements whose values are smallest among the elements, as determined by #<=>
or a given block.
max
: Returns the elements whose values are largest among the elements, as determined by #<=>
or a given block.
minmax
: Returns a 2-element Array
containing the smallest and largest elements.
min_by
: Returns the smallest element, as determined by the given block.
max_by
: Returns the largest element, as determined by the given block.
minmax_by
: Returns the smallest and largest elements, as determined by the given block.
Groups, slices, and partitions:
group_by
: Returns a Hash
that partitions the elements into groups.
partition
: Returns elements partitioned into two new Arrays, as determined by the given block.
slice_after
: Returns a new Enumerator
whose entries are a partition of self
, based either on a given object
or a given block.
slice_before
: Returns a new Enumerator
whose entries are a partition of self
, based either on a given object
or a given block.
slice_when
: Returns a new Enumerator
whose entries are a partition of self
based on the given block.
chunk
: Returns elements organized into chunks as specified by the given block.
chunk_while
: Returns elements organized into chunks as specified by the given block.
These methods return elements that meet a specified criterion:
find
(aliased as detect
): Returns an element selected by the block.
find_all
(aliased as filter
, select
): Returns elements selected by the block.
find_index
: Returns the index of an element selected by a given object or block.
reject
: Returns elements not rejected by the block.
uniq
: Returns elements that are not duplicates.
These methods return elements in sorted order:
sort
: Returns the elements, sorted by #<=>
or the given block.
sort_by
: Returns the elements, sorted by the given block.
each_entry
: Calls the block with each successive element (slightly different from each).
each_with_index
: Calls the block with each successive element and its index.
each_with_object
: Calls the block with each successive element and a given object.
each_slice
: Calls the block with successive non-overlapping slices.
each_cons
: Calls the block with successive overlapping slices. (different from each_slice
).
reverse_each
: Calls the block with each successive element, in reverse order.
collect
(aliased as map
): Returns objects returned by the block.
filter_map
: Returns truthy objects returned by the block.
flat_map
(aliased as collect_concat
): Returns flattened objects returned by the block.
grep
: Returns elements selected by a given object or objects returned by a given block.
grep_v
: Returns elements not selected by a given object or objects returned by a given block.
inject
(aliased as reduce
): Returns the object formed by combining all elements.
sum
: Returns the sum of the elements, using method +
.
zip
: Combines each element with elements from other enumerables; returns the n-tuples or calls the block with each.
cycle
: Calls the block with each element, cycling repeatedly.
To use module Enumerable in a collection class:
Include it:
include Enumerable
Implement method #each
which must yield successive elements of the collection. The method will be called by almost any Enumerable method.
Example:
class Foo include Enumerable def each yield 1 yield 1, 2 yield end end Foo.new.each_entry{ |element| p element }
Output:
1 [1, 2] nil
Ruby
Classes These Ruby
core classes include (or extend) Enumerable:
These Ruby
standard library classes include Enumerable:
CSV
CSV::Table
CSV::Row
Virtually all methods in Enumerable call method #each
in the including class:
Hash#each
yields the next key-value pair as a 2-element Array
.
Struct#each
yields the next name-value pair as a 2-element Array
.
For the other classes above, #each
yields the next object from the collection.
The example code snippets for the Enumerable methods:
When an operating system encounters an error, it typically reports the error as an integer error code:
$ ls nosuch.txt ls: cannot access 'nosuch.txt': No such file or directory $ echo $? # Code for last error. 2
When the Ruby
interpreter interacts with the operating system and receives such an error code (e.g., 2
), it maps the code to a particular Ruby
exception class (e.g., Errno::ENOENT
):
File.open('nosuch.txt') # => No such file or directory @ rb_sysopen - nosuch.txt (Errno::ENOENT)
Each such class is:
A nested class in this module, Errno
.
A subclass of class SystemCallError
.
Associated with an error code.
Thus:
Errno::ENOENT.superclass # => SystemCallError Errno::ENOENT::Errno # => 2
The names of nested classes are returned by method Errno.constants
:
Errno.constants.size # => 158 Errno.constants.sort.take(5) # => [:E2BIG, :EACCES, :EADDRINUSE, :EADDRNOTAVAIL, :EADV]
As seen above, the error code associated with each class is available as the value of a constant; the value for a particular class may vary among operating systems. If the class is not needed for the particular operating system, the value is zero:
Errno::ENOENT::Errno # => 2 Errno::ENOTCAPABLE::Errno # => 0
Each class in Errno
can be created with optional messages:
Errno::EPIPE.new # => #<Errno::EPIPE: Broken pipe> Errno::EPIPE.new("foo") # => #<Errno::EPIPE: Broken pipe - foo> Errno::EPIPE.new("foo", "here") # => #<Errno::EPIPE: Broken pipe @ here - foo>
See SystemCallError.new
.
The Warning
module contains a single method named warn
, and the module extends itself, making Warning.warn
available. Warning.warn
is called for all warnings issued by Ruby
. By default, warnings are printed to $stderr.
Changing the behavior of Warning.warn
is useful to customize how warnings are handled by Ruby
, for instance by filtering some warnings, and/or outputting warnings somewhere other than $stderr
.
If you want to change the behavior of Warning.warn
you should use Warning.extend(MyNewModuleWithWarnMethod)
and you can use super
to get the default behavior of printing the warning to $stderr
.
Example:
module MyWarningFilter def warn(message, category: nil, **kwargs) if /some warning I want to ignore/.match?(message) # ignore else super end end end Warning.extend MyWarningFilter
You should never redefine Warning#warn
(the instance method), as that will then no longer provide a way to use the default behavior.
The warning gem provides convenient ways to customize Warning.warn
.
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 coverage.
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)
Options script_safe
(boolean) specifies wether '\u2028'
, '\u2029'
and '/'
should be escaped as to make the JSON
object safe to interpolate in script tags.
Options ascii_only
(boolean) specifies wether all characters outside the ASCII range should be escaped.
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)
In concurrent programming, a monitor is an object or module intended to be used safely by more than one thread. The defining characteristic of a monitor is that its methods are executed with mutual exclusion. That is, at each point in time, at most one thread may be executing any of its methods. This mutual exclusion greatly simplifies reasoning about the implementation of monitors compared to reasoning about parallel code that updates a data structure.
You can read more about the general principles on the Wikipedia page for Monitors.
require 'monitor.rb' buf = [] buf.extend(MonitorMixin) empty_cond = buf.new_cond # consumer Thread.start do loop do buf.synchronize do empty_cond.wait_while { buf.empty? } print buf.shift end end end # producer while line = ARGF.gets buf.synchronize do buf.push(line) empty_cond.signal end end
The consumer thread waits for the producer thread to push a line to buf while buf.empty?
. The producer thread (main thread) reads a line from ARGF
and pushes it into buf then calls empty_cond.signal
to notify the consumer thread of new data.
Class
include require 'monitor' class SynchronizedArray < Array include MonitorMixin def initialize(*args) super(*args) end alias :old_shift :shift alias :old_unshift :unshift def shift(n=1) self.synchronize do self.old_shift(n) end end def unshift(item) self.synchronize do self.old_unshift(item) end end # other methods ... end
SynchronizedArray
implements an Array
with synchronized access to items. This Class
is implemented as subclass of Array
which includes the MonitorMixin
module.
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 after a specific object was 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 File.write 'private_key.pem', key.private_to_pem File.write 'public_key.pem', key.public_to_pem
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 password.
cipher = OpenSSL::Cipher.new 'aes-256-cbc' password = 'my secure password goes here' key_secure = key.private_to_pem cipher, password File.write 'private.secure.pem', key_secure
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 password when loading an encrypted key. If you will not be able to type in the password you may provide it when loading the key:
key4_pem = File.read 'private.secure.pem' password = 'my secure password goes here' key4 = OpenSSL::PKey.read key4_pem, password
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
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 password = 'my secure password goes here' cipher = 'aes-256-cbc' open 'ca_key.pem', 'w', 0400 do |io| io.write ca_key.private_to_pem(cipher, password) 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.
Namespace for file utility methods for copying, moving, removing, etc.
First, what’s elsewhere. Module FileUtils:
Inherits from class Object.
Supplements class File (but is not included or extended there).
Here, module FileUtils provides methods that are useful for:
::mkdir
: Creates directories.
::mkdir_p
, ::makedirs
, ::mkpath
: Creates directories, also creating ancestor directories as needed.
::link_entry
: Creates a hard link.
::ln_sf
: Creates symbolic links, overwriting if necessary.
::ln_sr
: Creates symbolic links relative to targets
::remove_dir
: Removes a directory and its descendants.
::remove_entry
: Removes an entry, including its descendants if it is a directory.
::remove_entry_secure
: Like ::remove_entry
, but removes securely.
::remove_file
: Removes a file entry.
::rm_f
, ::safe_unlink
: Like ::rm
, but removes forcibly.
::rm_r
: Removes entries and their descendants.
::rmdir
: Removes directories.
::uptodate?
: Returns whether a given entry is newer than given other entries.
::chmod
: Sets permissions for an entry.
::chmod_R
: Sets permissions for an entry and its descendants.
::chown
: Sets the owner and group for entries.
::chown_R
: Sets the owner and group for entries and their descendants.
::touch
: Sets modification and access times for entries, creating if necessary.
::compare_file
, ::cmp
, ::identical?
: Returns whether two entries are identical.
::compare_stream
: Returns whether two streams are identical.
::copy_entry
: Recursively copies an entry.
::copy_file
: Copies an entry.
::copy_stream
: Copies a stream.
::cp_lr
: Recursively creates hard links.
::cp_r
: Recursively copies files, retaining mode, owner, and group.
::install
: Recursively copies files, optionally setting mode, owner, and group.
::collect_method
: Returns the names of methods that accept a given option.
::commands
: Returns the names of methods that accept options.
::have_option?
: Returns whether a given method accepts a given option.
::options
: Returns all option names.
::options_of
: Returns the names of the options for a given method.
Some methods in FileUtils accept path arguments, which are interpreted as paths to filesystem entries:
If the argument is a string, that value is the path.
If the argument has method :to_path
, it is converted via that method.
If the argument has method :to_str
, it is converted via that method.
Some examples here involve trees of file entries. For these, we sometimes display trees using the tree command-line utility, which is a recursive directory-listing utility that produces a depth-indented listing of files and directories.
We use a helper method to launch the command and control the format:
def tree(dirpath = '.') command = "tree --noreport --charset=ascii #{dirpath}" system(command) end
To illustrate:
tree('src0') # => src0 # |-- sub0 # | |-- src0.txt # | `-- src1.txt # `-- sub1 # |-- src2.txt # `-- src3.txt
For certain methods that recursively remove entries, there is a potential vulnerability called the Time-of-check to time-of-use, or TOCTTOU, vulnerability that can exist when:
An ancestor directory of the entry at the target path is world writable; such directories include /tmp
.
The directory tree at the target path includes:
A world-writable descendant directory.
A symbolic link.
To avoid that vulnerability, you can use this method to remove entries:
FileUtils.remove_entry_secure
: removes recursively if the target path points to a directory.
Also available are these methods, each of which calls FileUtils.remove_entry_secure:
FileUtils.rm_r
with keyword argument secure: true
.
FileUtils.rm_rf
with keyword argument secure: true
.
Finally, this method for moving entries calls FileUtils.remove_entry_secure if the source and destination are on different file systems (which means that the “move” is really a copy and remove):
FileUtils.mv
with keyword argument secure: true
.
Method FileUtils.remove_entry_secure removes securely by applying a special pre-process:
If the target path points to a directory, this method uses methods File#chown
and File#chmod
in removing directories.
The owner of the target directory should be either the current process or the super user (root).
WARNING: You must ensure that ALL parent directories cannot be moved by other untrusted users. For example, parent directories should not be owned by untrusted users, and should not be world writable except when the sticky bit is set.
For details of this security vulnerability, see Perl cases:
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>
Module Open3 supports creating child processes with access to their $stdin, $stdout, and $stderr streams.
Each of these methods executes a given command in a new process or subshell, or multiple commands in new processes and/or subshells:
Each of these methods executes a single command in a process or subshell, accepts a string for input to $stdin, and returns string output from $stdout, $stderr, or both:
Open3.capture2
: Executes the command; returns the string from $stdout.
Open3.capture2e
: Executes the command; returns the string from merged $stdout and $stderr.
Open3.capture3
: Executes the command; returns strings from $stdout and $stderr.
Each of these methods executes a single command in a process or subshell, and returns pipes for $stdin, $stdout, and/or $stderr:
Open3.popen2
: Executes the command; returns pipes for $stdin and $stdout.
Open3.popen2e
: Executes the command; returns pipes for $stdin and merged $stdout and $stderr.
Open3.popen3
: Executes the command; returns pipes for $stdin, $stdout, and $stderr.
Each of these methods executes one or more commands in processes and/or subshells, returns pipes for the first $stdin, the last $stdout, or both:
Open3.pipeline_r
: Returns a pipe for the last $stdout.
Open3.pipeline_rw
: Returns pipes for the first $stdin and the last $stdout.
Open3.pipeline_w
: Returns a pipe for the first $stdin.
Open3.pipeline_start
: Does not wait for processes to complete.
Open3.pipeline
: Waits for processes to complete.
Each of the methods above accepts:
An optional hash of environment variable names and values; see Execution Environment.
A required string argument that is a command_line
or exe_path
; see Argument command_line or exe_path.
An optional hash of execution options; see Execution Options.
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.
© 2000 Network Applied Communication Laboratory, Inc.
© 2000 Information-technology Promotion Agency, Japan
The base exception for JSON
errors.
This exception is raised if the nesting of parsed data structures is too deep.