Validation problem

Hi All,

I've got an Expense model as follows:

class Expense < ActiveRecord::Base
  belongs_to :vendor

  def validate_on_create
    unless params["expense"]["vendor_id"] != "0" # <= Line 5
      errors.add("A vendor must be selected before creating the
expense record")
    end
  end
end

that crashes with:

undefined local variable or method `params' for #<Expense:0x4716c00>
[snip]
K:/_Projects/Ruby/_Rails_Apps/_EIMS/RTS/app/models/expense.rb:5:in
`validate_on_create'
[snip]
Parameters:

{"commit"=>"Create",
"expense"=>{"vendor_id"=>"0",

Is there some qualification I need to add to get at the parameters?

Thanks in Advance,
Richard

?? At that point your instance of Expense should already have its
vendor_id value -- kinda the point -- so

  unless self.vendor_id != 0 # or whatever -- what if it's nil? :slight_smile:

HTH,

Hi Hassan,

Thanks for your response.

At that point your instance of Expense should already have its

vendor_id value -- kinda the point -- so unless self.vendor_id != 0 #
or whatever -- what if it's nil? :slight_smile:

It can't be nil. When the form is instantiated, the Vendor drop-down
is initialized with:
1. vendor_id == 0
2. drop-down text = "=== Select a Vendor ==="

If the new expense record is to be created with vendor_id == 0, then
the user failed to associate a Vendor with the subject expense, and
that's invalid for this application. Therefore, the app needs to:
1. present an error-message at the top of the form
2. re-display all other form elements with their current content

Make sense?

Best wishes,
Richard

RichardOnRails wrote:

Hi All,

I've got an Expense model as follows:

class Expense < ActiveRecord::Base
  belongs_to :vendor

  def validate_on_create
    unless params["expense"]["vendor_id"] != "0" # <= Line 5
      errors.add("A vendor must be selected before creating the
expense record")
    end
  end
end

that crashes with:

undefined local variable or method `params' for #<Expense:0x4716c00>
[snip]

Your model by itself knows nothing about params received in the
controller. And by the time you're looking at validate_on_create, the
attributes from params have been assigned to the model, IIRC.

What about simply

def validate_on_create
  unless vendor_id != 0
    errors.add(blah blah blah)
  end
end

Assuredly, it can be -- a request can be generated without your form
being involved at all.

I'd suggest that it's good practice to handle any (even nil) input value
in a way appropriate to your business logic.

Or not, your call. Don't take my word for it. Write a test and see what
happens when you supply a nil value for that attribute. :slight_smile:

FWIW,

Thanks for your response. That fulfilled my implementation goal
perfectly.

Best wishes,
Richard

Hi Hassan,

> It can't be nil. When the form is instantiated

Assuredly, it can be -- a request can be generated without your form
being involved at all.

I don't get it. Can you point me to some tutorial that deals with
this issue?

I'd suggest that it's good practice to handle any (even nil) input value
in a way appropriate to your business logic.

I've code exactly the logic I wanted, except for how to enforce that a
vendor be selected from the drop-down before creating a new expense
record. What I didn't realize was that the validation occurs just
before a database row is added, so that the form's values could be
used for the validation. That's what Ar's post led me to understand.

Or not, your call. Don't take my word for it. Write a test and see what
happens when you supply a nil value for that attribute. :slight_smile:

I appreciate your suggestion as a way to improve my understanding.
But the issue I was stuck on is solved now, so I'm now rated to stick
ver. 1 of my app on website hosting service.

Best wishes,
Richard

RichardOnRails wrote:

Hi Hassan,

> It can't be nil. �When the form is instantiated

Assuredly, it can be -- a request can be generated without your form
being involved at all.

I don't get it. Can you point me to some tutorial that deals with
this issue?

What do you need a tutorial for? It's easy to use a Web browser or a
tool like curl to send an arbitrarily crafted GET or POST request that
never came from your form but looks as if it did. That's just the way
that a stateless protocol like HTTP works: each request is its own
universe and (absent hacks like cookies) doesn't keep track of what the
last request was.

Best,

Remember that all your form does is to build an http request and send
it to the server. A hacker can build any http request he likes
without using your form, and send it to your server, with any values
in params that he fancies.

Colin

Which you cannot do. You can "know" absolutely nothing about what
happens on the client side; all you can do is handle the requests that
you receive. Which may not include parameters that you're hoping to
see :slight_smile:

Hence my suggestion that your code deal appropriately with those
situations, however unlikely you might think they are.

Good luck,

Hi Marnen,

a tool like curl

I found a bunch of Google hits on it. I'll have to study them.

Thank for the tip,
Richard

Hi Colin,

Remember that all your form does is to build an http request and send

it to the server.

I didn't perceive that. Thanks for the enlightenment.

A hacker can build any http request he likes

without using your form, and send it to your server, with any values
in params that he fancies.

But that can be ameliorated by adding a User class with login/logout
methods with the former creating a User object. Then I can protect
attempted use of other than a User User#login from doing so absent a
User object. At least that's what I'm planning to implement next,
guided by Agile Web Dev w/ Rails, et al.

Sound reasonable?

Hi Hassan,

> I've code exactly the logic I wanted, except for how to enforce that a
> vendor be selected from the drop-down before creating a new expense
> record.

Which you cannot do. You can "know" absolutely nothing about what
happens on the client side; all you can do is handle the requests that
you receive. Which may not include parameters that you're hoping to
see :slight_smile:

I don't mean to be obstinate, Hassan. The problem is I don't see
what's wrong with what my code presently does:

My perception is that my browser renders a form with a drop-down for
vendor selection. That drop-down is populate via a
f.collection_select second parameter, vendors_for_exp (with no
argument).

The latter is a method defined in app\helpers\expenses_helper.rb as
follows:
  def vendors_for_exp(add_null_item = true)
    v = Vendor.find( :all, :order=>"nickname ASC")
    if add_null_item
      v0=Vendor.new; v0.id=0; v0.nickname = "--Select Vendor--"
      v.insert(0, v0)
    end
    v
  end

The values of all the fields are are stored in params[:expenses] as a
hash. In particular, the vendor instance selected by the user is
stored in params[:expenses][:vendor]. In particular, the id of that
Vendor instance is accessible as params[:expenses][:vendor][:id].
(I think strings or used instead of symbols for this stuff, but let's
ignore that.)

My problem was that even though my approach failed with Rails
displaying the params as I described the, the crash was in am
instance of an Expense model, so I

Hassan,

I accidentally cause my reply to fire before I complete my statement.
Here's the continuation:

My problem was that even though my approach failed with Rails
displaying the params as I described the, the crash was in am
instance of an Expense model, so I

I tried to get the ID of the Vendor instance referenced in the form
using the params hash. But that hash wasn't accessible to the model
instance. Hence the crash.

Pursuant to a previous reply to my posted question, I learned that I
could get the select-vendor's id with vendor_id in the
validate_on_create method of the Expense class instance and thus cause
an error message to be generated at the top of the form in the
browser. Testing shows that all this works fine.

So what do I fail to understand. (A lot, no doubt, but how much about
my validation of vendor selection on this form.)

Best wishes,
Richard

So what? That's totally missing the point. Your controller can receive
a request that has *absolutely no relation* to the form you created.

You could have a select for parameter "x" offering the choices 1, 2, or
3. It's utterly trivial for anyone, with any number of tools (cf. curl, wget)
to send a request with x=4, or x=the%20horse%20you%20rode%20in%20on
or x=''.

You have *no way* to control that. You *do* have the ability to validate
your inputs, and/or deal explicitly with exceptions if those inputs aren't
what you want. But you can't make assumptions that you *know* what
those inputs are going to be when they're coming from a remote client.

RichardOnRails wrote:

So what do I fail to understand. (A lot, no doubt, but how much about
my validation of vendor selection on this form.)

Best wishes,
Richard

On Aug 10, 3:30�pm, RichardOnRails

I think if you really want to see what Hassan is cautioning you about,
install Firefox as a browser if you don't already have it. Next, load
the Firebug tool for Firefox.

Now visit your apps web page in Firefox, then activate the Firebug
plug-in (click the little bug in the lower right corner).

Not only can you walk through your form within the Firebug window, but
you can change attributes of that form. Find your 'form action='
statement, double-click the action string within the quotes and voila,
you can alter the form target... or anything else you want... action,
method, values, etc.

Hi Ar,

I think if you really want to see what Hassan is cautioning you about ...

Another great response from you!! I've been doing my limited Internet
programming with a Desktop-programming mindset. But thanks to you, I
now see that I really have to validate each field in a response for
data-type, range, etc. regardless of how the form I provided the user
was set up.

Is that the correct mindset, as you perceive the problem?

Many thanks to you and Hassan,
Richard

Hi Marnen,

What do you need a tutorial for? It's easy to use a Web browser or a
tool like curl to send an arbitrarily crafted GET or POST request that
never came from your form but looks as if it did. That's just the way
that a stateless protocol like HTTP works: each request is its own
universe and (absent hacks like cookies) doesn't keep track of what the
last request was.

Thanks. I'm finally getting that idea through my thick skull. As I
said to another respondent, I was using the Desktop-Programming model
in the stateless Internet-Programming environment. Thanks to you, et
al, for getting that cleared up for me.

Best wishes,
Richard

Hi Colin,

> Hi Hassan,

>> > It can't be nil. When the form is instantiated

>> Assuredly, it can be -- a request can be generated without your form
>> being involved at all.

Hi Colin,

OK, OK. I finally get it. It took a lot of pounding to get me to see
the point of view you guys have been espousing. Thankfully, I'm
finally on board with you guys.

Best wishes,
Richard