Class Struct provides a convenient way to create a simple class that can store and fetch values.
This example creates a subclass of Struct
, Struct::Customer
; the first argument, a string, is the name of the subclass; the other arguments, symbols, determine the members of the new subclass.
Customer = Struct.new('Customer', :name, :address, :zip) Customer.name # => "Struct::Customer" Customer.class # => Class Customer.superclass # => Struct
Corresponding to each member are two methods, a writer and a reader, that store and fetch values:
methods = Customer.instance_methods false methods # => [:zip, :address=, :zip=, :address, :name, :name=]
An instance of the subclass may be created, and its members assigned values, via method ::new
:
joe = Customer.new("Joe Smith", "123 Maple, Anytown NC", 12345) joe # => #<struct Struct::Customer name="Joe Smith", address="123 Maple, Anytown NC", zip=12345>
The member values may be managed thus:
joe.name # => "Joe Smith" joe.name = 'Joseph Smith' joe.name # => "Joseph Smith"
And thus; note that member name may be expressed as either a string or a symbol:
joe[:name] # => "Joseph Smith" joe[:name] = 'Joseph Smith, Jr.' joe['name'] # => "Joseph Smith, Jr."
See Struct::new
.
What’s Here
First, what’s elsewhere. Class Struct:
-
Inherits from class Object.
-
Includes module Enumerable, which provides dozens of additional methods.
Here, class Struct provides methods that are useful for:
Methods for Creating a Struct
Subclass
::new
-
Returns a new subclass of Struct.
Methods for Querying
Methods for Comparing
- #==
-
Returns whether a given object is equal to
self
, using==
to compare member values. eql?
-
Returns whether a given object is equal to
self
, usingeql?
to compare member values.
Methods for Fetching
[]
-
Returns the value associated with a given member name.
to_a
,values
,deconstruct
-
Returns the member values in
self
as an array. deconstruct_keys
-
Returns a hash of the name/value pairs for given member names.
dig
-
Returns the object in nested objects that is specified by a given member name and additional arguments.
members
-
Returns an array of the member names.
select
,filter
-
Returns an array of member values from
self
, as selected by the given block. values_at
-
Returns an array containing values for given member names.
Methods for Assigning
[]=
-
Assigns a given value to a given member name.
Methods for Iterating
each
-
Calls a given block with each member name.
each_pair
-
Calls a given block with each member name/value pair.
Methods for Converting
Passwd
is a Struct
that contains the following members:
- name
-
contains the short login name of the user as a
String
. - passwd
-
contains the encrypted password of the user as a
String
. an ‘x’ is returned if shadow passwords are in use. An ‘*’ is returned if the user cannot log in using a password. - uid
-
contains the integer user ID (uid) of the user.
- gid
-
contains the integer group ID (gid) of the user’s primary group.
- dir
-
contains the path to the home directory of the user as a
String
. - shell
-
contains the path to the login shell of the user as a
String
.
The following members below are optional, and must be compiled with special flags:
- gecos
-
contains a longer
String
description of the user, such as a full name. Some Unix systems provide structured information in the gecos field, but this is system-dependent. must be compiled withHAVE_STRUCT_PASSWD_PW_GECOS
- change
-
password change time(integer) must be compiled with
HAVE_STRUCT_PASSWD_PW_CHANGE
- quota
-
quota value(integer) must be compiled with
HAVE_STRUCT_PASSWD_PW_QUOTA
- age
-
password age(integer) must be compiled with
HAVE_STRUCT_PASSWD_PW_AGE
- class
-
user access class(string) must be compiled with
HAVE_STRUCT_PASSWD_PW_CLASS
- comment
-
comment(string) must be compiled with
HAVE_STRUCT_PASSWD_PW_COMMENT
- expire
-
account expiration time(integer) must be compiled with
HAVE_STRUCT_PASSWD_PW_EXPIRE
Group
is a Struct
that is only available when compiled with HAVE_GETGRENT
.
The struct contains the following members:
- name
-
contains the name of the group as a
String
. - passwd
-
contains the encrypted password as a
String
. An ‘x’ is returned if password access to the group is not available; an empty string is returned if no password is needed to obtain membership of the group.Must be compiled with
HAVE_STRUCT_GROUP_GR_PASSWD
. - gid
-
contains the group’s numeric ID as an integer.
- mem
-
is an
Array
of Strings containing the short login names of the members of the group.
# File tmp/rubies/ruby-3.1.3/ext/json/lib/json/add/struct.rb, line 10
def self.json_create(object)
new(*object['v'])
end
static VALUE
rb_struct_s_keyword_init_p(VALUE obj)
{
}
Returns true
if the class was initialized with keyword_init: true
. Otherwise returns nil
or false
.
Examples:
Foo = Struct.new(:a) Foo.keyword_init? # => nil Bar = Struct.new(:a, keyword_init: true) Bar.keyword_init? # => true Baz = Struct.new(:a, keyword_init: false) Baz.keyword_init? # => false
static VALUE
rb_struct_s_members_m(VALUE klass)
{
VALUE members = rb_struct_s_members(klass);
return rb_ary_dup(members);
}
Returns the member names of the Struct
descendant as an array:
Customer = Struct.new(:name, :address, :zip) Customer.members # => [:name, :address, :zip]
static VALUE
rb_struct_s_def(int argc, VALUE *argv, VALUE klass)
{
VALUE name, rest, keyword_init = Qnil;
long i;
VALUE st;
st_table *tbl;
rb_check_arity(argc, 1, UNLIMITED_ARGUMENTS);
name = argv[0];
if (SYMBOL_P(name)) {
name = Qnil;
}
else {
--argc;
++argv;
}
if (RB_TYPE_P(argv[argc-1], T_HASH)) {
static ID keyword_ids[1];
if (!keyword_ids[0]) {
keyword_ids[0] = rb_intern("keyword_init");
}
rb_get_kwargs(argv[argc-1], keyword_ids, 0, 1, &keyword_init);
if (keyword_init == Qundef) {
keyword_init = Qnil;
}
else if (RTEST(keyword_init)) {
keyword_init = Qtrue;
}
--argc;
}
rest = rb_ident_hash_new();
RBASIC_CLEAR_CLASS(rest);
OBJ_WB_UNPROTECT(rest);
tbl = RHASH_TBL_RAW(rest);
for (i=0; i<argc; i++) {
VALUE mem = rb_to_symbol(argv[i]);
if (rb_is_attrset_sym(mem)) {
rb_raise(rb_eArgError, "invalid struct member: %"PRIsVALUE, mem);
}
if (st_insert(tbl, mem, Qtrue)) {
rb_raise(rb_eArgError, "duplicate member: %"PRIsVALUE, mem);
}
}
rest = rb_hash_keys(rest);
st_clear(tbl);
RBASIC_CLEAR_CLASS(rest);
OBJ_FREEZE_RAW(rest);
if (NIL_P(name)) {
st = anonymous_struct(klass);
}
else {
st = new_struct(name, klass);
}
setup_struct(st, rest);
rb_ivar_set(st, id_keyword_init, keyword_init);
if (rb_block_given_p()) {
rb_mod_module_eval(0, 0, st);
}
return st;
}
Struct.new
returns a new subclass of Struct
. The new subclass:
-
May be anonymous, or may have the name given by
class_name
. -
May have members as given by
member_names
. -
May have initialization via ordinary arguments (the default) or via keyword arguments (if
keyword_init: true
is given).
The new subclass has its own method ::new
; thus:
Foo = Struct.new('Foo', :foo, :bar) # => Struct::Foo f = Foo.new(0, 1) # => #<struct Struct::Foo foo=0, bar=1>
Class Name
With string argument class_name
, returns a new subclass of Struct
named Struct::class_name
:
Foo = Struct.new('Foo', :foo, :bar) # => Struct::Foo Foo.name # => "Struct::Foo" Foo.superclass # => Struct
Without string argument class_name
, returns a new anonymous subclass of Struct
:
Struct.new(:foo, :bar).name # => nil
Block
With a block given, the created subclass is yielded to the block:
Customer = Struct.new('Customer', :name, :address) do |new_class| p "The new subclass is #{new_class}" def greeting "Hello #{name} at #{address}" end end # => Struct::Customer dave = Customer.new('Dave', '123 Main') dave # => #<struct Struct::Customer name="Dave", address="123 Main"> dave.greeting # => "Hello Dave at 123 Main"
Output, from Struct.new
:
"The new subclass is Struct::Customer"
Member Names
Symbol arguments member_names
determines the members of the new subclass:
Struct.new(:foo, :bar).members # => [:foo, :bar] Struct.new('Foo', :foo, :bar).members # => [:foo, :bar]
The new subclass has instance methods corresponding to member_names
:
Foo = Struct.new('Foo', :foo, :bar) Foo.instance_methods(false) # => [:foo, :bar, :foo=, :bar=] f = Foo.new # => #<struct Struct::Foo foo=nil, bar=nil> f.foo # => nil f.foo = 0 # => 0 f.bar # => nil f.bar = 1 # => 1 f # => #<struct Struct::Foo foo=0, bar=1>
Singleton Methods
A subclass returned by Struct.new
has these singleton methods:
-
Method
::new
creates an instance of the subclass:Foo.new # => #<struct Struct::Foo foo=nil, bar=nil> Foo.new(0) # => #<struct Struct::Foo foo=0, bar=nil> Foo.new(0, 1) # => #<struct Struct::Foo foo=0, bar=1> Foo.new(0, 1, 2) # Raises ArgumentError: struct size differs
Method
::[]
is an alias for method::new
. -
Method
:inspect
returns a string representation of the subclass:Foo.inspect # => "Struct::Foo"
-
Method
::members
returns an array of the member names:Foo.members # => [:foo, :bar]
Keyword Argument
By default, the arguments for initializing an instance of the new subclass are ordinary arguments (not keyword arguments). With optional keyword argument keyword_init: true
, the new subclass is initialized with keyword arguments:
# Without keyword_init: true. Foo = Struct.new('Foo', :foo, :bar) Foo # => Struct::Foo Foo.new(0, 1) # => #<struct Struct::Foo foo=0, bar=1> # With keyword_init: true. Bar = Struct.new(:foo, :bar, keyword_init: true) Bar # => # => Bar(keyword_init: true) Bar.new(bar: 1, foo: 0) # => #<struct Bar foo=0, bar=1>
static VALUE
rb_struct_equal(VALUE s, VALUE s2)
{
if (s == s2) return Qtrue;
if (!RB_TYPE_P(s2, T_STRUCT)) return Qfalse;
if (rb_obj_class(s) != rb_obj_class(s2)) return Qfalse;
if (RSTRUCT_LEN(s) != RSTRUCT_LEN(s2)) {
rb_bug("inconsistent struct"); /* should never happen */
}
return rb_exec_recursive_paired(recursive_equal, s, s2, s2);
}
Returns true
if and only if the following are true; otherwise returns false
:
-
other.class == self.class
. -
For each member name
name
,other.name == self.name
.
Examples:
Customer = Struct.new(:name, :address, :zip) joe = Customer.new("Joe Smith", "123 Maple, Anytown NC", 12345) joe_jr = Customer.new("Joe Smith", "123 Maple, Anytown NC", 12345) joe_jr == joe # => true joe_jr[:name] = 'Joe Smith, Jr.' # => "Joe Smith, Jr." joe_jr == joe # => false
VALUE
rb_struct_aref(VALUE s, VALUE idx)
{
int i = rb_struct_pos(s, &idx);
if (i < 0) invalid_struct_pos(s, idx);
return RSTRUCT_GET(s, i);
}
Returns a value from self
.
With symbol or string argument name
given, returns the value for the named member:
Customer = Struct.new(:name, :address, :zip) joe = Customer.new("Joe Smith", "123 Maple, Anytown NC", 12345) joe[:zip] # => 12345
Raises NameError
if name
is not the name of a member.
With integer argument n
given, returns self.values[n]
if n
is in range; see Array Indexes:
joe[2] # => 12345 joe[-2] # => "123 Maple, Anytown NC"
Raises IndexError
if n
is out of range.
VALUE
rb_struct_aset(VALUE s, VALUE idx, VALUE val)
{
int i = rb_struct_pos(s, &idx);
if (i < 0) invalid_struct_pos(s, idx);
rb_struct_modify(s);
RSTRUCT_SET(s, i, val);
return val;
}
Assigns a value to a member.
With symbol or string argument name
given, assigns the given value
to the named member; returns value
:
Customer = Struct.new(:name, :address, :zip) joe = Customer.new("Joe Smith", "123 Maple, Anytown NC", 12345) joe[:zip] = 54321 # => 54321 joe # => #<struct Customer name="Joe Smith", address="123 Maple, Anytown NC", zip=54321>
Raises NameError
if name
is not the name of a member.
With integer argument n
given, assigns the given value
to the n
-th member if n
is in range; see Array Indexes:
joe = Customer.new("Joe Smith", "123 Maple, Anytown NC", 12345) joe[2] = 54321 # => 54321 joe[-3] = 'Joseph Smith' # => "Joseph Smith" joe # => #<struct Customer name="Joseph Smith", address="123 Maple, Anytown NC", zip=54321>
Raises IndexError
if n
is out of range.
# File tmp/rubies/ruby-3.1.3/ext/json/lib/json/add/struct.rb, line 16
def as_json(*)
klass = self.class.name
klass.to_s.empty? and raise JSON::JSONError, "Only named structs are supported!"
{
JSON.create_id => klass,
'v' => values,
}
end
Returns a hash, that will be turned into a JSON
object and represent this object.
static VALUE
rb_struct_deconstruct_keys(VALUE s, VALUE keys)
{
VALUE h;
long i;
if (NIL_P(keys)) {
return rb_struct_to_h(s);
}
if (UNLIKELY(!RB_TYPE_P(keys, T_ARRAY))) {
rb_raise(rb_eTypeError,
"wrong argument type %"PRIsVALUE" (expected Array or nil)",
rb_obj_class(keys));
}
if (RSTRUCT_LEN(s) < RARRAY_LEN(keys)) {
return rb_hash_new_with_size(0);
}
h = rb_hash_new_with_size(RARRAY_LEN(keys));
for (i=0; i<RARRAY_LEN(keys); i++) {
VALUE key = RARRAY_AREF(keys, i);
int i = rb_struct_pos(s, &key);
if (i < 0) {
return h;
}
rb_hash_aset(h, key, RSTRUCT_GET(s, i));
}
return h;
}
Returns a hash of the name/value pairs for the given member names.
Customer = Struct.new(:name, :address, :zip) joe = Customer.new("Joe Smith", "123 Maple, Anytown NC", 12345) h = joe.deconstruct_keys([:zip, :address]) h # => {:zip=>12345, :address=>"123 Maple, Anytown NC"}
Returns all names and values if array_of_names
is nil
:
h = joe.deconstruct_keys(nil) h # => {:name=>"Joseph Smith, Jr.", :address=>"123 Maple, Anytown NC", :zip=>12345}
static VALUE
rb_struct_dig(int argc, VALUE *argv, VALUE self)
{
rb_check_arity(argc, 1, UNLIMITED_ARGUMENTS);
self = rb_struct_lookup(self, *argv);
if (!--argc) return self;
++argv;
return rb_obj_dig(argc, argv, self, Qnil);
}
Finds and returns an object among nested objects. The nested objects may be instances of various classes. See Dig Methods.
Given symbol or string argument name
, returns the object that is specified by name
and identifiers
:
Foo = Struct.new(:a) f = Foo.new(Foo.new({b: [1, 2, 3]})) f.dig(:a) # => #<struct Foo a={:b=>[1, 2, 3]}> f.dig(:a, :a) # => {:b=>[1, 2, 3]} f.dig(:a, :a, :b) # => [1, 2, 3] f.dig(:a, :a, :b, 0) # => 1 f.dig(:b, 0) # => nil
Given integer argument n
, returns the object that is specified by n
and identifiers
:
f.dig(0) # => #<struct Foo a={:b=>[1, 2, 3]}> f.dig(0, 0) # => {:b=>[1, 2, 3]} f.dig(0, 0, :b) # => [1, 2, 3] f.dig(0, 0, :b, 0) # => 1 f.dig(:b, 0) # => nil
static VALUE
rb_struct_each(VALUE s)
{
long i;
RETURN_SIZED_ENUMERATOR(s, 0, 0, struct_enum_size);
for (i=0; i<RSTRUCT_LEN(s); i++) {
rb_yield(RSTRUCT_GET(s, i));
}
return s;
}
Calls the given block with the value of each member; returns self
:
Customer = Struct.new(:name, :address, :zip) joe = Customer.new("Joe Smith", "123 Maple, Anytown NC", 12345) joe.each {|value| p value }
Output:
"Joe Smith" "123 Maple, Anytown NC" 12345
Returns an Enumerator
if no block is given.
Related: each_pair
.
static VALUE
rb_struct_each_pair(VALUE s)
{
VALUE members;
long i;
RETURN_SIZED_ENUMERATOR(s, 0, 0, struct_enum_size);
members = rb_struct_members(s);
if (rb_block_pair_yield_optimizable()) {
for (i=0; i<RSTRUCT_LEN(s); i++) {
VALUE key = rb_ary_entry(members, i);
VALUE value = RSTRUCT_GET(s, i);
rb_yield_values(2, key, value);
}
}
else {
for (i=0; i<RSTRUCT_LEN(s); i++) {
VALUE key = rb_ary_entry(members, i);
VALUE value = RSTRUCT_GET(s, i);
rb_yield(rb_assoc_new(key, value));
}
}
return s;
}
Calls the given block with each member name/value pair; returns self
:
Customer = Struct.new(:name, :address, :zip) # => Customer joe = Customer.new("Joe Smith", "123 Maple, Anytown NC", 12345) joe.each_pair {|(name, value)| p "#{name} => #{value}" }
Output:
"name => Joe Smith" "address => 123 Maple, Anytown NC" "zip => 12345"
Returns an Enumerator
if no block is given.
Related: each
.
static VALUE
rb_struct_eql(VALUE s, VALUE s2)
{
if (s == s2) return Qtrue;
if (!RB_TYPE_P(s2, T_STRUCT)) return Qfalse;
if (rb_obj_class(s) != rb_obj_class(s2)) return Qfalse;
if (RSTRUCT_LEN(s) != RSTRUCT_LEN(s2)) {
rb_bug("inconsistent struct"); /* should never happen */
}
return rb_exec_recursive_paired(recursive_eql, s, s2, s2);
}
Returns true
if and only if the following are true; otherwise returns false
:
-
other.class == self.class
. -
For each member name
name
,other.name.eql?(self.name)
.Customer =
Struct.new
(:name, :address, :zip) joe = Customer.new(“Joe Smith”, “123 Maple, Anytown NC”, 12345) joe_jr = Customer.new(“Joe Smith”, “123 Maple, Anytown NC”, 12345) joe_jr.eql?(joe) # => true joe_jr = ‘Joe Smith, Jr.’ joe_jr.eql?(joe) # => false
Related: Object#==
.
static VALUE
rb_struct_hash(VALUE s)
{
long i, len;
st_index_t h;
VALUE n;
h = rb_hash_start(rb_hash(rb_obj_class(s)));
len = RSTRUCT_LEN(s);
for (i = 0; i < len; i++) {
n = rb_hash(RSTRUCT_GET(s, i));
h = rb_hash_uint(h, NUM2LONG(n));
}
h = rb_hash_end(h);
return ST2FIX(h);
}
Returns the integer hash value for self
.
Two structs of the same class and with the same content will have the same hash code (and will compare using Struct#eql?
):
Customer = Struct.new(:name, :address, :zip) joe = Customer.new("Joe Smith", "123 Maple, Anytown NC", 12345) joe_jr = Customer.new("Joe Smith", "123 Maple, Anytown NC", 12345) joe.hash == joe_jr.hash # => true joe_jr[:name] = 'Joe Smith, Jr.' joe.hash == joe_jr.hash # => false
Related: Object#hash
.
static VALUE
rb_struct_inspect(VALUE s)
{
return rb_exec_recursive(inspect_struct, s, 0);
}
Returns a string representation of self
:
Customer = Struct.new(:name, :address, :zip) # => Customer joe = Customer.new("Joe Smith", "123 Maple, Anytown NC", 12345) joe.inspect # => "#<struct Customer name=\"Joe Smith\", address=\"123 Maple, Anytown NC\", zip=12345>"
Struct#to_s
is an alias for Struct#inspect
.
static VALUE
rb_struct_members_m(VALUE obj)
{
return rb_struct_s_members_m(rb_obj_class(obj));
}
Returns the member names from self
as an array:
Customer = Struct.new(:name, :address, :zip) Customer.new.members # => [:name, :address, :zip]
Related: to_a
.
static VALUE
rb_struct_select(int argc, VALUE *argv, VALUE s)
{
VALUE result;
long i;
rb_check_arity(argc, 0, 0);
RETURN_SIZED_ENUMERATOR(s, 0, 0, struct_enum_size);
result = rb_ary_new();
for (i = 0; i < RSTRUCT_LEN(s); i++) {
if (RTEST(rb_yield(RSTRUCT_GET(s, i)))) {
rb_ary_push(result, RSTRUCT_GET(s, i));
}
}
return result;
}
With a block given, returns an array of values from self
for which the block returns a truthy value:
Customer = Struct.new(:name, :address, :zip) joe = Customer.new("Joe Smith", "123 Maple, Anytown NC", 12345) a = joe.select {|value| value.is_a?(String) } a # => ["Joe Smith", "123 Maple, Anytown NC"] a = joe.select {|value| value.is_a?(Integer) } a # => [12345]
With no block given, returns an Enumerator
.
Struct#filter
is an alias for Struct#select
.
VALUE
rb_struct_size(VALUE s)
{
return LONG2FIX(RSTRUCT_LEN(s));
}
Returns the number of members.
Customer = Struct.new(:name, :address, :zip) joe = Customer.new("Joe Smith", "123 Maple, Anytown NC", 12345) joe.size #=> 3
Struct#length
is an alias for Struct#size
.
static VALUE
rb_struct_to_a(VALUE s)
{
return rb_ary_new4(RSTRUCT_LEN(s), RSTRUCT_CONST_PTR(s));
}
Returns the values in self
as an array:
Customer = Struct.new(:name, :address, :zip) joe = Customer.new("Joe Smith", "123 Maple, Anytown NC", 12345) joe.to_a # => ["Joe Smith", "123 Maple, Anytown NC", 12345]
Struct#values
and Struct#deconstruct
are aliases for Struct#to_a
.
Related: members
.
static VALUE
rb_struct_to_h(VALUE s)
{
VALUE h = rb_hash_new_with_size(RSTRUCT_LEN(s));
VALUE members = rb_struct_members(s);
long i;
int block_given = rb_block_given_p();
for (i=0; i<RSTRUCT_LEN(s); i++) {
VALUE k = rb_ary_entry(members, i), v = RSTRUCT_GET(s, i);
if (block_given)
rb_hash_set_pair(h, rb_yield_values(2, k, v));
else
rb_hash_aset(h, k, v);
}
return h;
}
Returns a hash containing the name and value for each member:
Customer = Struct.new(:name, :address, :zip) joe = Customer.new("Joe Smith", "123 Maple, Anytown NC", 12345) h = joe.to_h h # => {:name=>"Joe Smith", :address=>"123 Maple, Anytown NC", :zip=>12345}
If a block is given, it is called with each name/value pair; the block should return a 2-element array whose elements will become a key/value pair in the returned hash:
h = joe.to_h{|name, value| [name.upcase, value.to_s.upcase]} h # => {:NAME=>"JOE SMITH", :ADDRESS=>"123 MAPLE, ANYTOWN NC", :ZIP=>"12345"}
Raises ArgumentError
if the block returns an inappropriate value.
# File tmp/rubies/ruby-3.1.3/ext/json/lib/json/add/struct.rb, line 27
def to_json(*args)
as_json.to_json(*args)
end
static VALUE
rb_struct_values_at(int argc, VALUE *argv, VALUE s)
{
return rb_get_values_at(s, RSTRUCT_LEN(s), argc, argv, struct_entry);
}
Returns an array of values from self
.
With integer arguments integers
given, returns an array containing each value given by one of integers
:
Customer = Struct.new(:name, :address, :zip) joe = Customer.new("Joe Smith", "123 Maple, Anytown NC", 12345) joe.values_at(0, 2) # => ["Joe Smith", 12345] joe.values_at(2, 0) # => [12345, "Joe Smith"] joe.values_at(2, 1, 0) # => [12345, "123 Maple, Anytown NC", "Joe Smith"] joe.values_at(0, -3) # => ["Joe Smith", "Joe Smith"]
Raises IndexError
if any of integers
is out of range; see Array Indexes.
With integer range argument integer_range
given, returns an array containing each value given by the elements of the range; fills with nil
values for range elements larger than the structure:
joe.values_at(0..2) # => ["Joe Smith", "123 Maple, Anytown NC", 12345] joe.values_at(-3..-1) # => ["Joe Smith", "123 Maple, Anytown NC", 12345] joe.values_at(1..4) # => ["123 Maple, Anytown NC", 12345, nil, nil]
Raises RangeError
if any element of the range is negative and out of range; see Array Indexes.