Can an initialize method fail?

I may be doing this wrong. :slight_smile: In some languages, I
might do something like:
聽聽user = User.new(name, password)

If the User class is unable to initialize a corresponding
user, it might not produce a valid User object.

What is a good way to do something like this? It seems that I could
do it by having User#initialize throw an exception, but my intuition
is that "user got password wrong" is not exactly an exceptional circumstance;
I have been told exceptions should be used only for unusual cases, and
should not be part of the fairly normal usage of a program.

My assumption that a "User" should be one who is logged in may not
be strictly necessary, but I rather like the way it simplifies everything
else -- this way, only one or two lines of code ever have to consider
the possibility of a user who didn't authenticate successfully.

So far as I can tell, though, the initialize method's return is
politely ignored. So. I perceive 4 options:

1. Throw an exception from User#initialize if the name and password
are invalid.
2. Add some kind of check immediately after this call which
verifies a successful login, and otherwise sets the local variable
user to nil.
3. Add a User#valid method or something similar, and check it all
over.
4. Ask ruby-talk, because surely I'm missing something obvious.

(Sorry about the stupid newbie questions; I promise to ask smart questions
once I've gotten better at this.)

-s

Peter Seebach wrote:

I may be doing this wrong. :slight_smile: In some languages, I
might do something like:
  user = User.new(name, password)

If the User class is unable to initialize a corresponding
user, it might not produce a valid User object.

What is a good way to do something like this? It seems that I could
do it by having User#initialize throw an exception, but my intuition
is that "user got password wrong" is not exactly an exceptional circumstance;
I have been told exceptions should be used only for unusual cases, and
should not be part of the fairly normal usage of a program.

My assumption that a "User" should be one who is logged in may not
be strictly necessary, but I rather like the way it simplifies everything
else -- this way, only one or two lines of code ever have to consider
the possibility of a user who didn't authenticate successfully.

So far as I can tell, though, the initialize method's return is
politely ignored.

<snip>

I'd use a different class method, like User.login, or some such. Then you can return either a valid User instance or nil as required. If you want to completely avoid exceptions, you'll either need to have a #valid? method in the instance, or actually do the credential validation at the class level (which seems slightly wrong to me, but there you go). It might look a little like this:

class User
   def User.login(name, password)
     u = User.new(name, password)
     return u.valid? ? u : nil
   end
end

or:

class User
   def User.login(name, password)
     can_log_in_with(name, password) ? User.new(name) : nil
   end

   def User.can_log_in_with(name, password)
     # do the name and password validation outside a User object here
   end
end

Of course, if you do go this route, you'll need to make sure that you never call User.new directly, and popping an exception under those circumstances is, I think, justified.

路路路

--
Alex

Oh, that might be a good choice. Might even be a good case for one
of those factory methods. :slight_smile: Hadn't thought of that, but I like it.
That might simplify some logic elsewhere.

-s

路路路

In message <46593D6A.5010300@blackkettle.org>, Alex Young writes:

I'd use a different class method, like User.login, or some such. Then
you can return either a valid User instance or nil as required. If you
want to completely avoid exceptions, you'll either need to have a
#valid? method in the instance, or actually do the credential validation
at the class level (which seems slightly wrong to me, but there you go).

I'd like to take that a bit further. I see two major aspects here: "initialization" and "login". "Initialization" of course includes creating an instance of class User and probably also fetching its state from some persistent location (file, database). Ideally this is done by some persistence framework.

"Login" is an application level concept. A user logs into an application. Part of this is creation of some form of session as a result of a successful login (be it a GUI object or some form of web application session). An important part of the login process is verification of the credentials the human user provides.

Now it depends on your application and how you model this. For example, your class User might be purely model or application aware. Both have their pros and cons ("model" is more flexible and has cleaner separation of concerns, "application" might be easier and less code).

One "clean separation" solution would be to have a class User with a method #password_ok? or something that accepts whatever the human user provides and returns a boolean result indicating that the credentials were ok or not. Instantiation is probably done via querying some form of persistence framework (e.g. db_session.query_user(:login=>"pseeb"). #login is probably a method in some application class that combines fetching the user and then verifying the password.

Of course there's a ton of ways to implement that. But IMHO it's useful to make oneself aware of all the aspects involved here. Also it made a nice example of the kinds of reasonings I like to apply to those cases. :slight_smile:

Kind regards

  robert

路路路

On 27.05.2007 10:37, Peter Seebach wrote:

In message <46593D6A.5010300@blackkettle.org>, Alex Young writes:

I'd use a different class method, like User.login, or some such. Then you can return either a valid User instance or nil as required. If you want to completely avoid exceptions, you'll either need to have a #valid? method in the instance, or actually do the credential validation at the class level (which seems slightly wrong to me, but there you go).

Oh, that might be a good choice. Might even be a good case for one
of those factory methods. :slight_smile: Hadn't thought of that, but I like it.
That might simplify some logic elsewhere.