Beginner help needed: use a class def to create new instance

I am attempting to put as much code inside classes as possible, to
economize the code in the main body. However, how do I create a new
class instance using only a method defined in a class?

For instance, in the following code, I want to create a new Class
Character instance named newchar by fleshing out the code in the #'d
area. Is it possible?

Otherwise, what is best practice?

PS: I realize I can instantiate newchar=Character.new(da da da) from the
body of the code but that would require the prompting code be there
also. that would defeat my purpose. I hope I have given this enough
thought before posting! :slight_smile:

Best Regards
Steve.

#!/usr/bin/ruby -w

class Character
  attr_reader :chname, :chnick, :chquote
  def initialize(chname,chnick)
    @chname=chname
    @chnick=chnick
    @chquote=String.new
  end
  def addquote(quote)
    @chquote=quote if quote.length > 10
  end
  def promptedinput
    print "Char name: ";chname=gets.chomp
    print "Char Nick: ";chnick=gets.chomp
    print "Char Quote: ";chquote=gets.chomp
    # Need help here!
                # What code goes here to add Character class instance?
    # class instance name should be newchar
  end
end

puts newchar.inspect #referencing added instance.

__END__

···

--
Posted via http://www.ruby-forum.com/.

Two considerations:

- I tend to separate the domain objects from the code that interacts
with the outside world to gather the data to populate that domain
object (user typing, DB, etc). This way you gain flexibility, being
able to add ways of loading or creating the object without modifying
that class

- If, in any case, you want the interaction with the user in that
class, I would use a class method that returns an instance. If you
have other ways of creating that instance you can add new methods for
each case. For example:

#!/usr/bin/ruby -w

class Character
  attr_reader :chname, :chnick, :chquote
  def initialize(chname,chnick)
@chname=chname
@chnick=chnick
@chquote=String.new
  end
def addquote(quote)
@chquote=quote if quote.length > 10
end
def self.from_prompted_input
print "Char name: ";chname=gets.chomp
print "Char Nick: ";chnick=gets.chomp
print "Char Quote: ";chquote=gets.chomp
    Character.new chname, chnick,chquote
  end
end

puts Character.from_prompted_input.inspect

What you had in mind couldn't work, because your method was an
instance method, so from the outside you still needed someone to call
Character.new(x,y,z).promptedinput.

Having class methods to instantiate objects of the class in different
ways is a pretty typical idiom, check for example the Date class in
core:

http://ruby-doc.org/core/classes/Date.html

The methods civil, commercial, etc are class methods that return an
instance, constructed in a different way.

Jesus.

···

On Tue, Jul 27, 2010 at 6:36 AM, Steve P. <stevepauly@comcast.net> wrote:

I am attempting to put as much code inside classes as possible, to
economize the code in the main body. However, how do I create a new
class instance using only a method defined in a class?

For instance, in the following code, I want to create a new Class
Character instance named newchar by fleshing out the code in the #'d
area. Is it possible?

Otherwise, what is best practice?

PS: I realize I can instantiate newchar=Character.new(da da da) from the
body of the code but that would require the prompting code be there
also. that would defeat my purpose. I hope I have given this enough
thought before posting! :slight_smile:

Steve P. wrote:

class Character
  attr_reader :chname, :chnick, :chquote
  def initialize(chname,chnick)
    @chname=chname
    @chnick=chnick
    @chquote=String.new
  end
  def addquote(quote)
    @chquote=quote if quote.length > 10
  end
  def promptedinput
    print "Char name: ";chname=gets.chomp
    print "Char Nick: ";chnick=gets.chomp
    print "Char Quote: ";chquote=gets.chomp
    # Need help here!
                # What code goes here to add Character class instance?
    # class instance name should be newchar
  end
end

I think you want a class method, not an instance method, since you're
creating a new object which doesn't have any relation to any existing
object.

class Character
  def initialize(chname,chnick,chquote="")
    @chname=chname
    @chnick=chnick
    @chquote=chquote
  end
  def self.promptedinput
    print "Char name: ";chname=gets.chomp
    print "Char Nick: ";chnick=gets.chomp
    print "Char Quote: ";chquote=gets.chomp
    new(chnam,chnick,chquote)
  end
end

newchar = Character.promptedinput
puts newchar.inspect

Note that I modified your initialize method to accept chquote as an
optional third argument. If you don't want to do that, then::

  def self.promptedinput
    print "Char name: ";chname=gets.chomp
    print "Char Nick: ";chnick=gets.chomp
    print "Char Quote: ";chquote=gets.chomp
    result = new(chname,chnick)
    result.chquote = chquote
    result
  end

···

--
Posted via http://www.ruby-forum.com/\.

Jesús Gabriel y Galán,

I was able to accomplish (at least crudely) moving code from the main
body to the class. I felt this was worth doing because I want as much
logic as possible in the class. (Is this a worthy thing??)

As you advised, I was able to create my new instance using a method that
returned an object.

Could you please take a look at how I implemented these two methods?
Account.setupaccount, Account.report

Is this how you would have structured it?
Any tips or comments greatly appreciated.
Steve.

#!/usr/bin/ruby
class Account
  attr_reader :nbr, :name, :coa
  def initialize(nbr,name,coa)
    @nbr=nbr
    @name=name
    setcoa(coa)
  end
  def setcoa(coa)
    if ["income", "expense", "asset", "liability",
     "capital"].include?(coa) then
      @coa=coa
    else
      puts "Error, defaulted to expense"
      @coa="expense"
    end
  end
  def Account.setupaccount
  #data entry routine inside class, not in main code body.
    print "enter acct nbr: ";nbr=gets.chomp
    print "enter name: ";name=gets.chomp
    print "enter presen: ";coa=gets.chomp
    return Account.new(nbr,name,coa)
  end
  def Account.report(acct)
  #reporting code inside class, not in main body.
    puts "===Account Number Listing==="
    puts "Account Nbr: #{acct.nbr}"
    puts "Account Name: #{acct.name}"
    puts "Account CofA: #{acct.coa}"
  end
end
##Start of main code body ##
newacct=Account.setupaccount
Account.report(newacct)
##End of Main code body ##

When I clear my confusion about this, I will do something like
class Ledger < Account
  ##code to make an array of the Account objects and add more
functionality
end

Jesús Gabriel y Galán wrote:

···

On Tue, Jul 27, 2010 at 6:36 AM, Steve P. <stevepauly@comcast.net> > wrote:

PS: I realize I can instantiate newchar=Character.new(da da da) from the
body of the code but that would require the prompting code be there
also. that would defeat my purpose. I hope I have given this enough
thought before posting! :slight_smile:

Two considerations:

- I tend to separate the domain objects from the code that interacts
with the outside world to gather the data to populate that domain
object (user typing, DB, etc). This way you gain flexibility, being
able to add ways of loading or creating the object without modifying
that class

- If, in any case, you want the interaction with the user in that
class, I would use a class method that returns an instance. If you
have other ways of creating that instance you can add new methods for
each case. For example:

#!/usr/bin/ruby -w

class Character
  attr_reader :chname, :chnick, :chquote
  def initialize(chname,chnick)
� @chname=chname
� � @chnick=chnick
� � @chquote=String.new
  end
� def addquote(quote)
� @chquote=quote if quote.length > 10
� end
� def self.from_prompted_input
� � print "Char name: � �";chname=gets.chomp
� � print "Char Nick: � �";chnick=gets.chomp
� � print "Char Quote: � � � � �";chquote=gets.chomp
    Character.new chname, chnick,chquote
  end
end

puts Character.from_prompted_input.inspect

What you had in mind couldn't work, because your method was an
instance method, so from the outside you still needed someone to call
Character.new(x,y,z).promptedinput.

Having class methods to instantiate objects of the class in different
ways is a pretty typical idiom, check for example the Date class in
core:

http://ruby-doc.org/core/classes/Date.html

The methods civil, commercial, etc are class methods that return an
instance, constructed in a different way.

Jesus.

--
Posted via http://www.ruby-forum.com/\.

Jesús Gabriel y Galán,

I was able to accomplish (at least crudely) moving code from the main
body to the class. I felt this was worth doing because I want as much
logic as possible in the class. (Is this a worthy thing??)

As you advised, I was able to create my new instance using a method that
returned an object.

Could you please take a look at how I implemented these two methods?
Account.setupaccount, Account.report

Is this how you would have structured it?
Any tips or comments greatly appreciated.
Steve.

#!/usr/bin/ruby
class Account
attr_reader :nbr, :name, :coa
def initialize(nbr,name,coa)
@nbr=nbr
@name=name
setcoa(coa)
end
def setcoa(coa)
if ["income", "expense", "asset", "liability",
"capital"].include?(coa) then
@coa=coa
else
puts "Error, defaulted to expense"
@coa="expense"
end
end

Maybe call this coa=, as it's a setter?

def coa=(coa)
....
end

def Account.setupaccount

def Account.setup_account is more idiomatic. Also I usually do def
self.setup_account, but that's a matter of taste, I guess.

#data entry routine inside class, not in main code body.
print "enter acct nbr: ";nbr=gets.chomp
print "enter name: ";name=gets.chomp
print "enter presen: ";coa=gets.chomp
return Account.new(nbr,name,coa)
end
def Account.report(acct)
#reporting code inside class, not in main body.
puts "===Account Number Listing==="
puts "Account Nbr: #{acct.nbr}"
puts "Account Name: #{acct.name}"
puts "Account CofA: #{acct.coa}"
end
end
##Start of main code body ##
newacct=Account.setupaccount
Account.report(newacct)
##End of Main code body ##

As I said, I don't really like having the user interaction inside the
domain class. For reporting, specially, I would create a to_s method
that formats the account info, and then have the caller of that method
be the one writing to stdout or to a file or whatever:

def to_s
s=<<EOF
===Account Number Listing===
Account Nbr: #{acct.nbr}
Account Name: #{acct.name}
Account CofA: #{acct.coa}
EOF
s
end

When I clear my confusion about this, I will do something like
class Ledger < Account
##code to make an array of the Account objects and add more
functionality
end

If you want an array of Account objects, you shouldn't inherit from
Account, but rather have an instance variable that holds an array of
Accounts:

class Ledger

  def initialize
    @accounts =
  end
  ...
end

Jesus.

···

On Sat, Jul 31, 2010 at 2:06 AM, Steve P. <stevepauly@comcast.net> wrote: