Forum: Ruby on Rails Two Customer Types - Best Design Principle?

Announcement (2017-05-07): www.ruby-forum.com is now read-only since I unfortunately do not have the time to support and maintain the forum any more. Please see rubyonrails.org/community and ruby-lang.org/en/community for other Rails- und Ruby-related community platforms.
54a6fc785e9fcf73ead8e387432b32f5?d=identicon&s=25 Dan Harper (Guest)
on 2006-03-14 19:58
(Received via mailing list)
Hi all,

I'll do my best to explain this...  I have the possibility to have two
customer types in my system:
1. A "mailing list" type: new records are saved when user enters email
address in the mailing list subscription form.
2. A "full" type: new records are saved when the user buys something and
provides all their billing/shipping details, etc.

Model:
 - I'd prefer to have 1 customers table with a column to mark their
status.

Validation:
 - The customer email address is common to both so validation on this is
easy.
 - I want the "full" type to have extended validation rules, first and
last name for example.
 - I'd like to enforce a unique email address.

Questions:
 - Rails looks to support model based validation well.  Is there a way
to specify validation rules depending on which customer type is being
saved (as I only want email validated for a mailing list type, but for
the full type I need validation on pretty much everything)?  Should I
have 2 models?  How do I map this to a single database table?
 - How do I code an upgrade on a customer?  For example, if the customer
is new and does not have an existing record, then when they buy
something and fill out their details there is no problem here (besides
the validation issue above).  But, if the customer does have an existing
record, is there an elegant way to "merge" the extended details from the
form with the existing email address marked record on the database?
Currently I have the code:

        form = params[:new_customer]
        email = form[:email]
        tmp_customer = Customer.find_by_email(email)
        if tmp_customer != nil && (tmp_customer.level == 'mailinglist'
|| tmp_customer.level == 'test')
            @customer = Customer.new(params[:new_customer])
            @customer.id = tmp_customer.id
            @customer.level = 'full'
            if @customer.update_attributes(@customer)
                flash[:notice] = "Customer details upgraded
successfully."
                redirect_to(:action => 'payment_details')
            else
                render(:action => 'checkout')
            end
        else
            @customer = Customer.new(params[:new_customer])
            @customer.level = 'full'
            if @customer.save
                flash[:notice] = "Customer details saved successfully."
                redirect_to(:action => 'payment_details')
            else
                render(:action => 'checkout')
            end
        end

But with this I get the error:  undefined method `stringify_keys!' for
#<Customer:0x38b1ac0>
My attempt was to get the existing customer, set the id of the form data
to the record on the database and then perform an update.  I'm not sure
however if I have covered all of the Rails requirements to do something
like this.  But, possibly (most likely due to my lack of Rails
experience), there is a better way.

Sorry for the long post, I hope it makes sense, can anyone help?

Thanks,
Dan
42172acdf3c6046f84d644cb0b94642c?d=identicon&s=25 Pat Maddox (pergesu)
on 2006-03-14 20:23
(Received via mailing list)
Hi Dan,

I did something like this in the past, and I took the approach of
separating list users from full users.  Logically they just didn't
seem the same to me.  I wrote a ListUser model which only had a name
and email address, and a User model which had all the info for a
regular user.  When I wanted to send an email to everyone, I'd just
pull all the email addresses from each table and mail it out.  This
setup also had the advantage of allowing me to direct different
messages to previous customers and potential customers - something
along the lines of, " Go to mysite.com/abcd to access the great deal
available only to current customers!" and "I see you aren't a customer
yet..well you're missing out on my ___ great deal.  Make an order by
6pm on Tuesday and you'll be able to take advantage of this killer
offer"

To avoid duplicate emails, when someone signed up for the site I would
just delete their entry in the list users table if it exists.  Worked
pretty nicely for me.

Pat
54a6fc785e9fcf73ead8e387432b32f5?d=identicon&s=25 Dan Harper (Guest)
on 2006-03-14 22:56
(Received via mailing list)
Interesting, thanks for your insight Pat.  I'll have a think about it.

I actually got part of it to work whilst hacking away on the train.  It
seems I wasn't thinking too straight on the first attempt and was over
complicating things.

If I change:
        form = params[:new_customer]
        email = form[:email]
        tmp_customer = Customer.find_by_email(email)
        if tmp_customer != nil && (tmp_customer.level == 'mailinglist'
|| tmp_customer.level == 'test')
            @customer = Customer.new(params[:new_customer])
            @customer.id = tmp_customer.id
            @customer.level = 'full'
            if @customer.update_attributes(@customer)
to:
        form = params[:new_customer]
        email = form[:email]
        tmp_customer = Customer.find_by_email(email)
        if tmp_customer != nil && (tmp_customer.level == 'mailinglist'
|| tmp_customer.level == 'test')
            if tmp_customer.update_attributes(params[:new_customer])

and include the level='full' as a hidden field in the form it works
beautifully!  I think that the error was generated because the new
customer (@customer) didn't have an id value because Rails only
generates it just before it is written to the database.  Because I'm
already checking that the email address matches, it doesn't matter if it
gets overwritten in the update (this issue led to a bit of reverse
thinking in the first and broken solution).

But still an issue is the validation issue.  Pat's solution certainly
gets around the problem.  Can anyone else chip in with some advice?

Thanks,
Dan
B3260ee62969961010117e21e9872a3a?d=identicon&s=25 Kenneth Lee (Guest)
on 2006-03-14 23:17
(Received via mailing list)
I think you want to check out some of the writings on Single Table
Inheritance

- http://wiki.rubyonrails.com/rails/pages/SingleTabl...
- http://www.martinfowler.com/eaaCatalog/singleTable...

Rails supports STI -- check out ActiveRecord::Base under STI...
54a6fc785e9fcf73ead8e387432b32f5?d=identicon&s=25 Dan Harper (Guest)
on 2006-03-15 00:06
(Received via mailing list)
Ah ha!  That looks exactly like what I need.  Thanks Kenneth.

Dan
This topic is locked and can not be replied to.