Topic: Ruby Newbie Problem

I'm new to Ruby, so I'm sure this is an easy one.

I have the following class:

class Pilot < ActiveRecord::Base
  validates_presence_of :last_name, :first_name, :middle_name, :birth_date
  has_many :flights, :dependent => :destroy 
  has_many :aircraft
  
  def flight_count
    return flights.length
  end

  def full_name
    puts "full_name()"
    name = ""
  if @first_name != nil then 
    name += @first_name 
  end
    puts "@first_name = \"#{@first_name}\" name = \"#{name}\""
  if @middle_name != nil then 
    name += " " + @middle_name 
  end
    puts "@middle_name = \"#{@middle_name}\" name = \"#{name}\""
  if @last_name != nil then 
    name += " " + @last_name 
  end
    puts "@last_name = \"#{@last_name}\" name = \"#{name}\""
    puts "full_name() returns \"#{name}\""
  return name
  end
  
  def inspect
  return to_s
  end

  def to_s
  return "Pilot [#{id}](name = \"#{self.full_name}\" birth_date = " + (birth_date == nil ? "nil" : birth_date.to_formatted_s(:short)) + " flights = " + flight_count.to_s + ")"
  end
end

When I call the full_name method from a web page, it returns the full name as expected.  But when I call if from the to_s method, it returns an empty string because first_name, last_name, and middle_name are empty strings (but they shouldn't be).  Here's the console output:

C:\Ruby\projects\logbook>ruby script/server
=> Booting Mongrel
=> Rails 2.3.5 application starting on [url]http://0.0.0.0:3000[/url]
=> Call with -d to detach
=> Ctrl-C to shutdown server
new() pilot_id: "2"
full_name()
@first_name = "" name = ""
@middle_name = "" name = ""
@last_name = "" name = ""
full_name() returns ""
pilot = "Pilot [2](name = "" birth_date = 12 Oct flights = 1)"
full_name()
@first_name = "" name = ""
@middle_name = "" name = ""
@last_name = "" name = ""
full_name() returns ""
pilot = "Pilot [2](name = "" birth_date = 12 Oct flights = 1)"
full_name()
@first_name = "" name = ""
@middle_name = "" name = ""
@last_name = "" name = ""
full_name() returns ""


Processing FlightsController#new (for 127.0.0.1 at 2010-04-13 21:46:55) [GET]
  Parameters: {"pilot_id"=>"2"}
  ←[4;36;1mPilot Load (0.0ms)←[0m   ←[0;1mSELECT * FROM "pilots" WHERE ("pilots"
."id" = 2) ←[0m
  ←[4;35;1mFlight Load (0.0ms)←[0m   ←[0mSELECT * FROM "flights" WHERE ("flights
".pilot_id = 2) ←[0m
Rendering template within layouts/flights
Rendering flights/new
Rendered flights/_form (1047.0ms)
Completed in 2422ms (View: 1359, DB: 0) | 200 OK [http://localhost/pilots/2/flig
hts/new]

Any ideas what I'm doing wrong?

Last edited by kcav8or (2010-04-13 23:02:42)

Re: Ruby Newbie Problem

Another clue:  My web page references a method on my Flight class called "pilot_name".  When that method calls Pilot.full_name, it returns an empty string.  However, when I simply concatenate the three name fields together, it returns the name correctly.  So it appears to be a problem with how the Pilot.full_name method is declared or called.

Re: Ruby Newbie Problem

Few things.

In case you don't know, an explicit return is not needed in Ruby (in the way you are using it) - the last expression in a block/method definition is the returned. If you want to continue using an explicit return, then go ahead, but as a heads up, it is not Ruby convention.

Your code for full_name is driving me crazy - sorry, it just is. It can be shortened to (minus debug messages & there are probably better ways):

def full_name
  [ first_name, middle_name, last_name ].compact.join( " " )
end

Also, I would not personally rewrite inspect to be identical to to_s. If you want to display the object as a string, use to_s, if you want to inspect it's properties/state for debugging/whatever then use inspect and I would leave it as implemented by Rails.

Now, onto your problem. I have been out of the rails loop for a while, but it might be something to do with the fact you are using instance variables, instead of the accessor methods for first_name, etc. You should always use the accessor methods. Try that and let me know if it worked.

If I've helped you out with something, feel free to recommend me on working with rails.

Re: Ruby Newbie Problem

No offense taken at all.  Like I said, I'm new to this language.  Good suggestion on the full_name method, BTW.  I knew Ruby had a lot of nice shortcuts like that, but wasn't familiar with that one.

I've generated the accessors using attr_accessor, but now my web page won't display the names as it did before.  Here's the new Pilot class:

class Pilot < ActiveRecord::Base
  attr_accessor :first_name, :middle_name, :last_name
  validates_presence_of :last_name, :first_name, :middle_name, :birth_date
  has_many :flights, :dependent => :destroy 
  has_many :aircraft
  
  def flight_count
    return flights.length
  end

  def full_name
    [ first_name, middle_name, last_name ].compact.join( " " )
  end
  
  def to_s
  self.full_name
  end
end

... and here's the web page:

<h1>Listing pilots</h1>

<table>
  <tr>
    <th>First name</th>
    <th>Middle name</th>
    <th>Last name</th>
    <th>Birth date</th>
    <th>Flights</th>
  </tr>

<% @pilots.each do |pilot| %>
  <tr>
    <td><%=h pilot.first_name %></td>
    <td><%=h pilot.middle_name %></td>
    <td><%=h pilot.last_name %></td>
    <td><%=h pilot.birth_date %></td>
    <td><%=h pilot.flight_count %></td>
    <td><%= link_to 'Show', pilot %></td>
    <td><%= link_to 'Edit', edit_pilot_path(pilot) %></td>
    <td><%= link_to 'Destroy', pilot, :confirm => 'Are you sure?', :method => :delete %></td>
  </tr>
<% end %>
</table>

<br />

<%= link_to 'New pilot', new_pilot_path %>

So I'm still missing something.  Without the accessors the name fields display correctly.  With them, they are blank.

Re: Ruby Newbie Problem

kcav8or wrote:

No offense taken at all.  Like I said, I'm new to this language.  Good suggestion on the full_name method, BTW.  I knew Ruby had a lot of nice shortcuts like that, but wasn't familiar with that one.

I've generated the accessors using attr_accessor, but now my web page won't display the names as it did before.  Here's the new Pilot class:

class Pilot < ActiveRecord::Base
  attr_accessor :first_name, :middle_name, :last_name
  validates_presence_of :last_name, :first_name, :middle_name, :birth_date
  has_many :flights, :dependent => :destroy 
  has_many :aircraft
  
  def flight_count
    return flights.length
  end

  def full_name
    [ first_name, middle_name, last_name ].compact.join( " " )
  end
  
  def to_s
  self.full_name
  end
end

... and here's the web page:

<h1>Listing pilots</h1>

<table>
  <tr>
    <th>First name</th>
    <th>Middle name</th>
    <th>Last name</th>
    <th>Birth date</th>
    <th>Flights</th>
  </tr>

<% @pilots.each do |pilot| %>
  <tr>
    <td><%=h pilot.first_name %></td>
    <td><%=h pilot.middle_name %></td>
    <td><%=h pilot.last_name %></td>
    <td><%=h pilot.birth_date %></td>
    <td><%=h pilot.flight_count %></td>
    <td><%= link_to 'Show', pilot %></td>
    <td><%= link_to 'Edit', edit_pilot_path(pilot) %></td>
    <td><%= link_to 'Destroy', pilot, :confirm => 'Are you sure?', :method => :delete %></td>
  </tr>
<% end %>
</table>

<br />

<%= link_to 'New pilot', new_pilot_path %>

So I'm still missing something.  Without the accessors the name fields display correctly.  With them, they are blank.

The accessor methods will be generated automatically by rails if they are fields in the corresponding database table; and if everything works when you don't declare your own, then I guess the problem is solved. wink

If I've helped you out with something, feel free to recommend me on working with rails.

Re: Ruby Newbie Problem

Ninju wrote:

The accessor methods will be generated automatically by rails if they are fields in the corresponding database table; and if everything works when you don't declare your own, then I guess the problem is solved. wink

The way this works (or doesn't work) is as follows:  if a class inherits ActiveRecord::Base, then on class load time, ruby looks at the corresponding database table, gets the fields, and dynamically generates get and set methods for each field (among other things).  Ie, if you have a first_name field in the database, then ruby will add this code to the class:


def first_name
  #get first name out of db and put it into the object's temporary data (ie instance variables)
end

def first_name=(string)
  #set object's temporary data value for first name to string - this will be written to the db on object.save
end


This happens straight away, ie it's the first thing that's done on instantiating the class.  Then, ruby goes through each line in turn, setting up any of the other class level stuff.  So,  if it then encounters this:

attr_accessor :first_name

it does the standard ruby behaviour for attr_accessor, which is to add this code:


def first_name
  @first_name
end

def first_name=(string)
  @first_name = string
end

In ruby, you can define a method as many times as you want, and the last version to be defined is the only one that counts: it overrides the others.  So, in this case the second pair of definitions override the first, and you lose the 'interface with the database' versions, in favour of the more simple ruby versions.

Last edited by Max Williams (2010-04-19 06:39:08)

###########################################
#If i've helped you then please recommend me at Working With Rails:
#http://www.workingwithrails.com/person/ … i-williams

Re: Ruby Newbie Problem

"This happens straight away, ie it's the first thing that's done on instantiating the class.  Then, ruby goes through each line in turn, setting up any of the other class level stuff. "

I just want to say, for full disclosure, that the above isn't quite what really happens - what really happens is to do with a list of places where each object looks for method definitions, and 'defined later' could really be rewritten as 'appearing first in the list of definitions' (the list is like a stack, ie things you encounter earlier are the things that were added most recently).  But from a practical point of view it's a useful way to think about what happens.

Last edited by Max Williams (2010-04-19 06:45:21)

###########################################
#If i've helped you then please recommend me at Working With Rails:
#http://www.workingwithrails.com/person/ … i-williams