From: Wes Bailey on

----- "William Song" <wei_song1990(a)hotmail.com> wrote:

> How can I properly define a struct in a class?
>
> If I have
>
> class Cards
> Card = Struct.new(:suit, :number)
> @cardsOnHand=[]
>
> @cardsOnHand.push(Card.new("S","5"))
> end
>
> This will give me an error message saying the push is not defined for
> NilClass. How should I fix this?
> --
> Posted via http://www.ruby-forum.com/.

Your problem is not a struct but the fact that you are not properly instantiating an instance variable. I probably wouldn't use a struct like you have instead solve the problem understand that your cards class is a deck that structurally is just an array:

class Deck < Array
def add( card )
raise ArgumentError unless card.is_a?( Card )
# Other checks on how many cards per suite and not allowing duplicate cards

self.push( card )
end
end

class Card
attr_accessor :number, :suite

def initialize( number, suite )
@number = number.to_s if /[2-9AKQJ]/.match( number.to_s )
@suite = suite.to_s if /[CHDS]/.match( suite.to_s )

raise Exception if @number.nil? || @suite.nil?
end
end

deck = Deck.new

deck.add( Card.new( 3, :H ) )
deck.add( Card.new( '4', 'S' ) )


From: Rein Henrichs on
On 2010-06-08 14:02:03 -0700, Wes Bailey said:

> ----- "William Song" <wei_song1990(a)hotmail.com> wrote:
>
>> How can I properly define a struct in a class?
>>
>> If I have
>>
>> class Cards
>> Card = Struct.new(:suit, :number)
>> @cardsOnHand=[]
>>
>> @cardsOnHand.push(Card.new("S","5"))
>> end
>>
>> This will give me an error message saying the push is not defined for
>> NilClass. How should I fix this?
>> --
>> Posted via http://www.ruby-forum.com/.
>
> Your problem is not a struct but the fact that you are not properly
> instantiating an instance variable. I probably wouldn't use a struct
> like you have instead solve the problem understand that your cards
> class is a deck that structurally is just an array:
>
> class Deck < Array
> def add( card )
> raise ArgumentError unless card.is_a?( Card )
> # Other checks on how many cards per suite and not allowing duplicate cards
>
> self.push( card )
> end
> end
>
> class Card
> attr_accessor :number, :suite
>
> def initialize( number, suite )
> @number = number.to_s if /[2-9AKQJ]/.match( number.to_s )
> @suite = suite.to_s if /[CHDS]/.match( suite.to_s )
>
> raise Exception if @number.nil? || @suite.nil?
> end
> end
>
> deck = Deck.new
>
> deck.add( Card.new( 3, :H ) )
> deck.add( Card.new( '4', 'S' ) )
>

The fact that the Deck class violates the Liskov Substitution Principle
should imply that a Deck is not necessarily an Array. For instance, the
semantics of many Array and Enumerable methods would be undefined on a
deck of cards. It would be better to create a Deck class that delegated
semantically appropriate methods to an internal array object (using
Forwardable, for instance) rather than expecting a Deck to conform to
the entire Array interface.

This is why it is, in general, somewhat dubious to subclass base
classes for your own use, even when there seems to be a superficial
similarity.

Of course, none of this has anything to do with the original problem.

--
Rein Henrichs
http://puppetlabs.com
http://reinh.com

From: Wes Bailey on
[Note: parts of this message were removed to make it a legal post.]


----- "Rein Henrichs" <reinh(a)reinh.com> wrote:
> On 2010-06-08 14:02:03 -0700, Wes Bailey said:
>
> > ----- "William Song" <wei_song1990(a)hotmail.com> wrote:
> >
> >> How can I properly define a struct in a class?
> >>
> >> If I have
> >>
> >> class Cards
> >> Card = Struct.new(:suit, :number)
> >> @cardsOnHand=[]
> >>
> >> @cardsOnHand.push(Card.new("S","5"))
> >> end
> >>
> >> This will give me an error message saying the push is not defined for
> >> NilClass. How should I fix this?
> >> --
> >> Posted via http://www.ruby-forum.com/.
> >
> > Your problem is not a struct but the fact that you are not properly
> > instantiating an instance variable. I probably wouldn't use a struct
> > like you have instead solve the problem understand that your cards
> > class is a deck that structurally is just an array:
> >
> > class Deck < Array
> > def add( card )
> > raise ArgumentError unless card.is_a?( Card )
> > # Other checks on how many cards per suite and not allowing duplicate cards
> >
> > self.push( card )
> > end
> > end
> >
> > class Card
> > attr_accessor :number, :suite
> >
> > def initialize( number, suite )
> > @number = number.to_s if /[2-9AKQJ]/.match( number.to_s )
> > @suite = suite.to_s if /[CHDS]/.match( suite.to_s )
> >
> > raise Exception if @number.nil? || @suite.nil?
> > end
> > end
> >
> > deck = Deck.new
> >
> > deck.add( Card.new( 3, :H ) )
> > deck.add( Card.new( '4', 'S' ) )
> >
>
> The fact that the Deck class violates the Liskov Substitution Principle
> should imply that a Deck is not necessarily an Array. For instance, the
> semantics of many Array and Enumerable methods would be undefined on a
> deck of cards. It would be better to create a Deck class that delegated
> semantically appropriate methods to an internal array object (using
> Forwardable, for instance) rather than expecting a Deck to conform to
> the entire Array interface.
>
> This is why it is, in general, somewhat dubious to subclass base
> classes for your own use, even when there seems to be a superficial
> similarity.
>
> Of course, none of this has anything to do with the original problem.
>
> --
> Rein Henrichs
> http://puppetlabs.com
> http://reinh.com


Thanks for pointing that out Rein. You learn something new every day when active in this mailing list. Forwardable might be possible but I think you are right that Deck is really its own class that uses an array object as the internal data store.