Easy Question, I Think [re-post, sorry if dupe]

I am just getting started with Rails, don’t know any Ruby, and don’t
quite even get object oriented programming yet. I have tweaked my
schema to The Rails Way and have generated a bit of scaffolding.

I am trying to create a new Part, which references a PartName and a
PartNumber. I can create a PartNumber on the fly, but the PartName
has to exist. I have an inelegant solution that works, but does not
properly exploit the validation because I don’t get back the
appropriate errors when a PartName is not existent or a PartNumber
fails to be created. All I get is that the Part was missing the key
values.

Is there a simpler way to design the :create such that the referenced
records will be created if they don’t exist (or fail if they don’t
exist in the case of PartName) and return relevant error information
to the user?

I know this is all wrong, but would love a few pointers on the right
way to do it!!

Thank you in advance…

  • Ian

-----------------Schema ------------------

CREATE TABLE part_names (
id serial primary key,
part_name character varying NOT NULL,
group_id integer not null,
description character varying,
idx_part_name tsvector
);

ALTER TABLE part_names ADD CONSTRAINT pn_group FOREIGN KEY (group_id)
REFERENCES groups(id) DEFERRABLE INITIALLY DEFERRED;

CREATE TABLE part_numbers (
id serial primary key,
part_number integer NOT NULL,
constraint chk_part_number check (part_number ~’[0-9]*’)
);

CREATE TABLE parts (
id serial primary key,
part_number_id integer NOT NULL,
part_name_id integer NOT NULL,
quantity integer DEFAULT 1 NOT NULL,
side character varying,
size character varying,
“comment” character varying,
constraint chk_side check (side in (‘L.H.’, ‘R.H.’) or side is null)
);

ALTER TABLE parts ADD CONSTRAINT parts_pnumber_fkey FOREIGN KEY
(part_number_id) REFERENCES part_numbers(id) DEFERRABLE INITIALLY
DEFERRED;
ALTER TABLE parts ADD CONSTRAINT parts_pname_fkey FOREIGN KEY
(part_name_id) REFERENCES part_names(id) DEFERRABLE INITIALLY
DEFERRED;

---------------------Models--------------------

class Part < ActiveRecord::Base
belongs_to :part_name
belongs_to :part_number
has_many :part_years
has_many :part_details
has_many :part_comments
has_many :car_job_parts
validates_inclusion_of :side,
:allow_nil => true,
:in => %w{ L.H. R.H. }
validates_inclusion_of :quantity,
:in => 1…99
validates_presence_of :part_number_id, :part_name_id
validates_associated :part_number, :part_name
validates_uniqueness_of :part_number_id, :scope =>
“part_name_id”
end

class PartNumber < ActiveRecord::Base
has_many :parts
has_many :part_number_images
has_many :part_number_illustrations
has_and_belongs_to_many :vendor_parts
validates_numericality_of :part_number
validates_inclusion_of :part_number, :in => 111111…99999999
validates_uniqueness_of :part_number
end

class PartName < ActiveRecord::Base
belongs_to :group
has_and_belongs_to_many :jobs
has_many :parts
validates_presence_of :part_name, :group_id
# validates_associated :groups <-- causes infinite loop!
validates_associated :parts
validates_uniqueness_of :part_name
end

---------------------View ---------------------
_form.html

<%= error_messages_for ‘part’ %>

Part Name
<%= text_field_with_auto_complete :part_name, :part_name %>

Part Number
<%= text_field_with_auto_complete :part_number, :part_number %>

Quantity
<%= text_field 'part', 'quantity' %>

Side
<%= text_field 'part', 'side' %>

Size
<%= text_field 'part', 'size' %>

Comment
<%= text_field_with_auto_complete :part, :comment %>

-------------------Controller---------------------------

class PartsController < ApplicationController

auto_complete_for :part, :comment
auto_complete_for :part_number, :part_number
auto_complete_for :part_name, :part_name

def index
list
render :action => ‘list’
end

def list
@part_pages, @parts = paginate :parts, :order => “part_name_id”,
:per_page => 10
end

def show
@part = Part.find(params[:id])
end

def new
@part = Part.new
@part_name = PartName.new
@part_number = PartNumber.new
end

def create
@part = Part.new(params[:part])
@part_name = PartName.new(params[:part_name])
@part_number = PartNumber.new(params[:part_number])

# I dont want to create a part name because there is more to it than 

this
# but I want creation to fail. It does, but the field is not
highlighted
# because the object is not specifically invalidated?

@part.part_name = 

PartName.find_by_part_name(params[:part_name][:part_name])

# The find portion of this seems to try to find the record,

regardless of whether
# the input is of the wrong type. I suppose that makes sense, but
there must
# be an easier way than explicitly calling .valid?

if @part_number.valid?
  @part.part_number =

PartNumber.find_or_create_by_part_number(params[:part_number][:part_number])
end

if @part.save
  flash[:notice] = 'Part was successfully created.'
  redirect_to :action => 'list'
else
  render :action => 'new'
end

end

def edit
@part = Part.find(params[:id])
end

def update
@part = Part.find(params[:id])
if @part.update_attributes(params[:part])
flash[:notice] = ‘Part was successfully updated.’
redirect_to :action => ‘show’, :id => @part
else
render :action => ‘edit’
end
end

def destroy
Part.find(params[:id]).destroy
redirect_to :action => ‘list’
end
end


"Her faults were those of her race and sex; her virtues were her own.
Farewell, and if for ever - "

– “Travels with a Donkey in the Cevennes” by Robert Louis Stevenson