class Struct

Class Struct provides a convenient way to create a simple class that can store and fetch values.

This example creates a subclass ofStruct,Struct::Customer; the first argument, a string, is the name of the subclass; the other arguments, symbols, determine themembers of the new subclass.

Customer =Struct.new('Customer',:name,:address,:zip)Customer.name# => "Struct::Customer"Customer.class# => ClassCustomer.superclass# => Struct

Corresponding to each member are two methods, a writer and a reader, that store and fetch values:

methods =Customer.instance_methodsfalsemethods# => [: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."

SeeStruct::new.

What’s Here

First, what’s elsewhere. Class Struct:

See alsoData, which is a somewhat similar, but stricter concept for defining immutable value objects.

Here, class Struct provides methods that are useful for:

Methods for Creating aStruct Subclass

Methods for Querying

Methods for Comparing

Methods for Fetching

Methods for Assigning

Methods for Iterating

Methods for Converting

Public Class Methods

Source
# File ext/json/lib/json/add/struct.rb, line 9defself.json_create(object)new(*object['v'])end

Seeas_json.

Source
static VALUErb_struct_s_keyword_init_p(VALUE obj){}

Returnstrue if the class was initialized withkeyword_init: true. Otherwise returnsnil orfalse.

Examples:

Foo =Struct.new(:a)Foo.keyword_init?# => nilBar =Struct.new(:a,keyword_init:true)Bar.keyword_init?# => trueBaz =Struct.new(:a,keyword_init:false)Baz.keyword_init?# => false
Source
static VALUErb_struct_s_members_m(VALUE klass){    VALUE members = rb_struct_s_members(klass);    return rb_ary_dup(members);}

Returns the member names of theStruct descendant as an array:

Customer =Struct.new(:name,:address,:zip)Customer.members# => [:name, :address, :zip]
Source
static VALUErb_struct_s_def(int argc, VALUE *argv, VALUE klass){    VALUE name = Qnil, rest, keyword_init = Qnil;    long i;    VALUE st;    VALUE opt;    argc = rb_scan_args(argc, argv, "0*:", NULL, &opt);    if (argc >= 1 && !SYMBOL_P(argv[0])) {        name = argv[0];        --argc;        ++argv;    }    if (!NIL_P(opt)) {        static ID keyword_ids[1];        if (!keyword_ids[0]) {            keyword_ids[0] = rb_intern("keyword_init");        }        rb_get_kwargs(opt, keyword_ids, 0, 1, &keyword_init);        if (UNDEF_P(keyword_init)) {            keyword_init = Qnil;        }        else if (RTEST(keyword_init)) {            keyword_init = Qtrue;        }    }    rest = rb_ident_hash_new();    RBASIC_CLEAR_CLASS(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 (RTEST(rb_hash_has_key(rest, mem))) {            rb_raise(rb_eArgError, "duplicate member: %"PRIsVALUE, mem);        }        rb_hash_aset(rest, mem, Qtrue);    }    rest = rb_hash_keys(rest);    RBASIC_CLEAR_CLASS(rest);    OBJ_FREEZE(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 ofStruct. The new subclass:

  • May be anonymous, or may have the name given byclass_name.

  • May have members as given bymember_names.

  • May have initialization via ordinary arguments, or via keyword arguments

The new subclass has its own method::new; thus:

Foo =Struct.new('Foo',:foo,:bar)# => Struct::Foof =Foo.new(0,1)# => #<struct Struct::Foo foo=0, bar=1>

Class Name

With string argumentclass_name, returns a new subclass ofStruct namedStruct::class_name:

Foo =Struct.new('Foo',:foo,:bar)# => Struct::FooFoo.name# => "Struct::Foo"Foo.superclass# => Struct

Without string argumentclass_name, returns a new anonymous subclass ofStruct:

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}"defgreeting"Hello #{name} at #{address}"endend# => Struct::Customerdave =Customer.new('Dave','123 Main')dave# =>     #<struct Struct::Customer name="Dave", address="123 Main">dave.greeting# => "Hello Dave at 123 Main"

Output, fromStruct.new:

"The new subclass is Struct::Customer"

Member Names

Symbol argumentsmember_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 tomember_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# => nilf.foo =0# => 0f.bar# => nilf.bar =1# => 1f# => #<struct Struct::Foo foo=0, bar=1>

Singleton Methods

A subclass returned byStruct.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# Initialization with keyword arguments:Foo.new(foo:0)# => #<struct Struct::Foo foo=0, bar=nil>Foo.new(foo:0,bar:1)# => #<struct Struct::Foo foo=0, bar=1>Foo.new(foo:0,bar:1,baz:2)# Raises ArgumentError: unknown keywords: baz
  • 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 can be both positional and keyword arguments.

Optional keyword argumentkeyword_init: allows to force only one type of arguments to be accepted:

KeywordsOnly =Struct.new(:foo,:bar,keyword_init:true)KeywordsOnly.new(bar:1,foo:0)# => #<struct KeywordsOnly foo=0, bar=1>KeywordsOnly.new(0,1)# Raises ArgumentError: wrong number of argumentsPositionalOnly =Struct.new(:foo,:bar,keyword_init:false)PositionalOnly.new(0,1)# => #<struct PositionalOnly foo=0, bar=1>PositionalOnly.new(bar:1,foo:0)# => #<struct PositionalOnly foo={:foo=>1, :bar=>2}, bar=nil># Note that no error is raised, but arguments treated as one hash value# Same as not providing keyword_init:Any =Struct.new(:foo,:bar,keyword_init:nil)Any.new(foo:1,bar:2)# => #<struct Any foo=1, bar=2>Any.new(1,2)# => #<struct Any foo=1, bar=2>

Public Instance Methods

Source
static VALUErb_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);}

Returnstrue if and only if the following are true; otherwise returnsfalse:

  • other.class == self.class.

  • For each member namename,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# => truejoe_jr[:name] ='Joe Smith, Jr.'# => "Joe Smith, Jr."joe_jr==joe# => false
Source
VALUErb_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 fromself.

With symbol or string argumentname 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

RaisesNameError ifname is not the name of a member.

With integer argumentn given, returnsself.values[n] ifn is in range; seeArray Indexes atArray:

joe[2]# => 12345joe[-2]# => "123 Maple, Anytown NC"

RaisesIndexError ifn is out of range.

Source
VALUErb_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 argumentname given, assigns the givenvalue to the named member; returnsvalue:

Customer =Struct.new(:name,:address,:zip)joe =Customer.new("Joe Smith","123 Maple, Anytown NC",12345)joe[:zip] =54321# => 54321joe# => #<struct Customer name="Joe Smith", address="123 Maple, Anytown NC", zip=54321>

RaisesNameError ifname is not the name of a member.

With integer argumentn given, assigns the givenvalue to then-th member ifn is in range; seeArray Indexes atArray:

joe =Customer.new("Joe Smith","123 Maple, Anytown NC",12345)joe[2] =54321# => 54321joe[-3] ='Joseph Smith'# => "Joseph Smith"joe# => #<struct Customer name="Joseph Smith", address="123 Maple, Anytown NC", zip=54321>

RaisesIndexError ifn is out of range.

Source
# File ext/json/lib/json/add/struct.rb, line 30defas_json(*)klass =self.class.nameklass.to_s.empty?andraiseJSON::JSONError,"Only named structs are supported!"  {JSON.create_id=>klass,'v'=>values,  }end

MethodsStruct#as_json andStruct.json_create may be used to serialize and deserialize a Struct object; seeMarshal.

MethodStruct#as_json serializesself, returning a 2-element hash representingself:

require'json/add/struct'Customer =Struct.new('Customer',:name,:address,:zip)x =Struct::Customer.new.as_json# => {"json_class"=>"Struct::Customer", "v"=>[nil, nil, nil]}

MethodJSON.create deserializes such a hash, returning a Struct object:

Struct::Customer.json_create(x)# => #<struct Struct::Customer name=nil, address=nil, zip=nil>
Alias for:to_a
Source
static VALUErb_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 ifarray_of_names isnil:

h =joe.deconstruct_keys(nil)h# => {:name=>"Joseph Smith, Jr.", :address=>"123 Maple, Anytown NC", :zip=>12345}
Source
static VALUErb_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. SeeDig Methods.

Given symbol or string argumentname, returns the object that is specified byname andidentifiers:

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)# => 1f.dig(:b,0)# => nil

Given integer argumentn, returns the object that is specified byn andidentifiers:

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)# => 1f.dig(:b,0)# => nil
Source
static VALUErb_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; returnsself:

Customer =Struct.new(:name,:address,:zip)joe =Customer.new("Joe Smith","123 Maple, Anytown NC",12345)joe.each {|value|pvalue }

Output:

"Joe Smith""123 Maple, Anytown NC"12345

Returns anEnumerator if no block is given.

Related:each_pair.

Source
static VALUErb_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; returnsself:

Customer =Struct.new(:name,:address,:zip)# => Customerjoe =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 anEnumerator if no block is given.

Related:each.

Source
static VALUErb_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);}

Returnstrue if and only if the following are true; otherwise returnsfalse:

  • other.class == self.class.

  • For each member namename,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)# => truejoe_jr[:name] ='Joe Smith, Jr.'joe_jr.eql?(joe)# => false

Related:Object#==.

Alias for:select
Source
static VALUErb_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 forself.

Two structs of the same class and with the same content will have the same hash code (and will compare usingStruct#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# => truejoe_jr[:name] ='Joe Smith, Jr.'joe.hash==joe_jr.hash# => false

Related:Object#hash.

Source
static VALUErb_struct_inspect(VALUE s){    return rb_exec_recursive(inspect_struct, s, rb_str_new2("#<struct "));}

Returns a string representation ofself:

Customer =Struct.new(:name,:address,:zip)# => Customerjoe =Customer.new("Joe Smith","123 Maple, Anytown NC",12345)joe.inspect# => "#<struct Customer name=\"Joe Smith\", address=\"123 Maple, Anytown NC\", zip=12345>"
Also aliased as:to_s
Alias for:size
Source
static VALUErb_struct_members_m(VALUE obj){    return rb_struct_s_members_m(rb_obj_class(obj));}

Returns the member names fromself as an array:

Customer =Struct.new(:name,:address,:zip)Customer.new.members# => [:name, :address, :zip]

Related:to_a.

Source
static VALUErb_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 fromself 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 anEnumerator.

Also aliased as:filter
Source
VALUErb_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
Also aliased as:length
Source
static VALUErb_struct_to_a(VALUE s){    return rb_ary_new4(RSTRUCT_LEN(s), RSTRUCT_CONST_PTR(s));}

Returns the values inself 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]

Related:members.

Also aliased as:values,deconstruct
Source
static VALUErb_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_hh# => {: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"}

RaisesArgumentError if the block returns an inappropriate value.

Source
# File ext/json/lib/json/add/struct.rb, line 49defto_json(*args)as_json.to_json(*args)end

Returns aJSON string representingself:

require'json/add/struct'Customer =Struct.new('Customer',:name,:address,:zip)putsStruct::Customer.new.to_json

Output:

{"json_class":"Struct","t":{'name':'Rowdy',"age":null}}
Alias for:inspect
Alias for:to_a
Source
static VALUErb_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 fromself.

With integer argumentsintegers given, returns an array containing each value given by one ofintegers:

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"]

RaisesIndexError if any ofintegers is out of range; seeArray Indexes atArray.

With integer range argumentinteger_range given, returns an array containing each value given by the elements of the range; fills withnil 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]

RaisesRangeError if any element of the range is negative and out of range; seeArray Indexes atArray.