1 # depends on: class.rb enumerable.rb hash.rb
13 # Struct.new( [aString] [, aSym]+> ) => StructClass
14 # StructClass.new(arg, ...) => obj
15 # StructClass[arg, ...] => obj
17 # Creates a new class, named by <em>aString</em>, containing accessor
18 # methods for the given symbols. If the name <em>aString</em> is omitted,
19 # an anonymous structure class will be created. Otherwise, the name of
20 # this struct will appear as a constant in class <tt>Struct</tt>, so it
21 # must be unique for all <tt>Struct</tt>s in the system and should start
22 # with a capital letter. Assigning a structure class to a constant
23 # effectively gives the class the name of the constant.
25 # <tt>Struct::new</tt> returns a new <tt>Class</tt> object, which can then
26 # be used to create specific instances of the new structure. The number of
27 # actual parameters must be less than or equal to the number of attributes
28 # defined for this class; unset parameters default to \nil{}. Passing too
29 # many parameters will raise an \E{ArgumentError}.
31 # The remaining methods listed in this section (class and instance) are
32 # defined for this generated class.
34 # # Create a structure with a name in Struct
35 # Struct.new("Customer", :name, :address) #=> Struct::Customer
36 # Struct::Customer.new("Dave", "123 Main") #=> #<Struct::Customer
37 # name="Dave", address="123 Main">
38 # # Create a structure named by its constant
39 # Customer = Struct.new(:name, :address) #=> Customer
40 # Customer.new("Dave", "123 Main") #=> #<Customer
41 # name="Dave", address="123 Main">
43 def self.new(klass_name, *attrs, &block)
44 unless klass_name.nil? then
46 klass_name = StringValue klass_name
48 attrs.unshift klass_name
54 attrs = attrs.map { |attr| attr.to_sym }
55 rescue NoMethodError => e
56 raise TypeError, e.message
59 raise ArgumentError if attrs.any? { |attr| attr.nil? }
61 klass = Class.new self do
66 return subclass_new(*args)
75 Struct.const_set klass_name, klass if klass_name
77 klass.const_set :STRUCT_ATTRS, attrs
79 klass.module_eval(&block) if block
84 def self.allocate # :nodoc:
89 return self.class.const_get(:STRUCT_ATTRS)
92 def instance_variables
93 # Hide the ivars used to store the struct fields
94 super() - _attrs.map { |a| "@#{a}" }
98 raise ArgumentError unless args.length <= _attrs.length
99 _attrs.each_with_index do |attr, i|
100 instance_variable_set "@#{attr}", args[i]
108 # struct == other_struct => true or false
110 # Equality---Returns <tt>true</tt> if <em>other_struct</em> is equal to
111 # this one: they must be of the same class as generated by
112 # <tt>Struct::new</tt>, and the values of all instance variables must be
113 # equal (according to <tt>Object#==</tt>).
115 # Customer = Struct.new(:name, :address, :zip)
116 # joe = Customer.new("Joe Smith", "123 Maple, Anytown NC", 12345)
117 # joejr = Customer.new("Joe Smith", "123 Maple, Anytown NC", 12345)
118 # jane = Customer.new("Jane Doe", "456 Elm, Anytown NC", 12345)
119 # joe == joejr #=> true
120 # joe == jane #=> false
123 return false if (self.class != other.class)
124 return false if (self.values.size != other.values.size)
126 self.values.size.times { |i|
127 next if (RecursionGuard.inspecting?(self.values.at(i)))
128 next if (RecursionGuard.inspecting?(other.values.at(i)))
129 RecursionGuard.inspect(self.values.at(i)) do
130 RecursionGuard.inspect(other.values.at(i)) do
131 return false if (self.values.at(i) != other.values.at(i))
140 # struct[symbol] => anObject
141 # struct[fixnum] => anObject
143 # Attribute Reference---Returns the value of the instance variable named
144 # by <em>symbol</em>, or indexed (0..length-1) by <em>fixnum</em>. Will
145 # raise <tt>NameError</tt> if the named variable does not exist, or
146 # <tt>IndexError</tt> if the index is out of range.
148 # Customer = Struct.new(:name, :address, :zip)
149 # joe = Customer.new("Joe Smith", "123 Maple, Anytown NC", 12345)
150 # joe["name"] #=> "Joe Smith"
151 # joe[:name] #=> "Joe Smith"
152 # joe[0] #=> "Joe Smith"
158 a_len = _attrs.length
159 if var > a_len - 1 then
160 raise IndexError, "offset #{var} too large for struct(size:#{a_len})"
163 raise IndexError, "offset #{var + a_len} too small for struct(size:#{a_len})"
166 when Symbol, String then
173 unless _attrs.include? var.to_sym then
174 raise NameError, "no member '#{var}' in struct"
177 return instance_variable_get("@#{var}")
182 # struct[symbol] = obj => obj
183 # struct[fixnum] = obj => obj
185 # Attribute Assignment---Assigns to the instance variable named by
186 # <em>symbol</em> or <em>fixnum</em> the value <em>obj</em> and returns
187 # it. Will raise a <tt>NameError</tt> if the named variable does not
188 # exist, or an <tt>IndexError</tt> if the index is out of range.
190 # Customer = Struct.new(:name, :address, :zip)
191 # joe = Customer.new("Joe Smith", "123 Maple, Anytown NC", 12345)
192 # joe["name"] = "Luke"
193 # joe[:zip] = "90210"
194 # joe.name #=> "Luke"
195 # joe.zip #=> "90210"
201 a_len = _attrs.length
202 if var > a_len - 1 then
203 raise IndexError, "offset #{var} too large for struct(size:#{a_len})"
206 raise IndexError, "offset #{var + a_len} too small for struct(size:#{a_len})"
209 when Symbol, String then
216 unless _attrs.include? var.to_s.intern then
217 raise NameError, "no member '#{var}' in struct"
220 return instance_variable_set("@#{var}", obj)
225 # struct.each {|obj| block } => struct
227 # Calls <em>block</em> once for each instance variable, passing the value
230 # Customer = Struct.new(:name, :address, :zip)
231 # joe = Customer.new("Joe Smith", "123 Maple, Anytown NC", 12345)
232 # joe.each {|x| puts(x) }
237 # 123 Maple, Anytown NC
241 return values.each(&block)
246 # struct.each_pair {|sym, obj| block } => struct
248 # Calls <em>block</em> once for each instance variable, passing the name
249 # (as a symbol) and the value as parameters.
251 # Customer = Struct.new(:name, :address, :zip)
252 # joe = Customer.new("Joe Smith", "123 Maple, Anytown NC", 12345)
253 # joe.each_pair {|name, value| puts("#{name} => #{value}") }
258 # address => 123 Maple, Anytown NC
262 raise LocalJumpError unless block_given? # HACK yield should do this
263 _attrs.map { |var| yield var, instance_variable_get("@#{var}") }
272 # struct.eql?(other) => true or false
274 # Two structures are equal if they are the same object, or if all their
275 # fields are equal (using <tt>eql?</tt>).
278 return true if self == other
279 return false if self.class != other.class
285 # struct.hash => fixnum
287 # Return a hash value based on this struct's contents.
295 # struct.length => fixnum
296 # struct.size => fixnum
298 # Returns the number of instance variables.
300 # Customer = Struct.new(:name, :address, :zip)
301 # joe = Customer.new("Joe Smith", "123 Maple, Anytown NC", 12345)
312 # struct.members => array
314 # Returns an array of strings representing the names of the instance
317 # Customer = Struct.new(:name, :address, :zip)
318 # joe = Customer.new("Joe Smith", "123 Maple, Anytown NC", 12345)
319 # joe.members #=> ["name", "address", "zip"]
322 return const_get(:STRUCT_ATTRS).map { |member| member.to_s }
326 return self.class.members
331 # struct.select(fixnum, ... ) => array
332 # struct.select {|i| block } => array
334 # The first form returns an array containing the elements in
335 # <em>struct</em> corresponding to the given indices. The second form
336 # invokes the block passing in successive elements from <em>struct</em>,
337 # returning an array containing those elements for which the block returns
338 # a true value (equivalent to <tt>Enumerable#select</tt>).
340 # Lots = Struct.new(:a, :b, :c, :d, :e, :f)
341 # l = Lots.new(11, 22, 33, 44, 55, 66)
342 # l.select(1, 3, 5) #=> [22, 44, 66]
343 # l.select(0, 2, 4) #=> [11, 33, 55]
344 # l.select(-1, -3, -5) #=> [66, 44, 22]
345 # l.select {|v| (v % 2).zero? } #=> [22, 44, 66]
353 # struct.to_a => array
354 # struct.values => array
356 # Returns the values for this instance as an array.
358 # Customer = Struct.new(:name, :address, :zip)
359 # joe = Customer.new("Joe Smith", "123 Maple, Anytown NC", 12345)
360 # joe.to_a[1] #=> "123 Maple, Anytown NC"
363 return _attrs.map { |var| instance_variable_get "@#{var}" }
368 # struct.to_s => string
369 # struct.inspect => string
371 # Describe the contents of this struct in a string.
374 return "[...]" if RecursionGuard.inspecting?(self)
376 RecursionGuard.inspect(self) do
377 "#<struct #{self.class.name} #{_attrs.zip(self.to_a).map{|o| o[1] = o[1].inspect; o.join('=')}.join(', ') }>"
385 # struct.to_a => array
386 # struct.values => array
388 # Returns the values for this instance as an array.
390 # Customer = Struct.new(:name, :address, :zip)
391 # joe = Customer.new("Joe Smith", "123 Maple, Anytown NC", 12345)
392 # joe.to_a[1] #=> "123 Maple, Anytown NC"
398 # struct.values_at(selector,... ) => an_array
400 # Returns an array containing the elements in <em>self</em> corresponding
401 # to the given selector(s). The selectors may be either integer indices or
402 # ranges. See also </code>.select<code>.
404 # a = %w{ a b c d e f }
405 # a.values_at(1, 3, 5)
406 # a.values_at(1, 3, 5, 7)
407 # a.values_at(-1, -3, -5, -7)
408 # a.values_at(1..3, 2...5)
411 to_a.values_at(*args)