# Two Customer Types - Best Design Principle?

**URL:** <https://www.ruby-forum.com/t/two-customer-types-best-design-principle/54841>\
**Category:** Rails\
**Created:** [March 14, 2006, 7:58pm UTC](https://www.ruby-forum.com/t/two-customer-types-best-design-principle/54841 "2006-03-14T19:58:45Z")\
**Posts on this page:** 5\
**Page:** 1

<div class="post-metadata">

**Author:** ![danhobbs](https://www.ruby-forum.com/letter_avatar_proxy/v4/letter/d/a5b964/32.png) [@danhobbs](https://www.ruby-forum.com/u/danhobbs)\
**Post date:** [March 14, 2006, 7:58pm UTC](https://www.ruby-forum.com/t/two-customer-types-best-design-principle/54841/1 "2006-03-14T19:58:45Z")

</div>

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:

|| 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

---

<div class="post-metadata">

**Author:** ![danhobbs](https://www.ruby-forum.com/letter_avatar_proxy/v4/letter/d/a5b964/32.png) [@danhobbs](https://www.ruby-forum.com/u/danhobbs)\
**Post date:** [March 14, 2006, 8:23pm UTC](https://www.ruby-forum.com/t/two-customer-types-best-design-principle/54841/2 "2006-03-14T20:23:20Z")

</div>

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](http://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

---

<div class="post-metadata">

**Author:** ![danhobbs](https://www.ruby-forum.com/letter_avatar_proxy/v4/letter/d/a5b964/32.png) [@danhobbs](https://www.ruby-forum.com/u/danhobbs)\
**Post date:** [March 14, 2006, 10:56pm UTC](https://www.ruby-forum.com/t/two-customer-types-best-design-principle/54841/3 "2006-03-14T22:56:31Z")

</div>

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

---

<div class="post-metadata">

**Author:** ![danhobbs](https://www.ruby-forum.com/letter_avatar_proxy/v4/letter/d/a5b964/32.png) [@danhobbs](https://www.ruby-forum.com/u/danhobbs)\
**Post date:** [March 15, 2006, 12:06am UTC](https://www.ruby-forum.com/t/two-customer-types-best-design-principle/54841/4 "2006-03-15T00:06:52Z")

</div>

Ah ha! That looks exactly like what I need. Thanks Kenneth.

Dan

---

<div class="post-metadata">

**Author:** ![danhobbs](https://www.ruby-forum.com/letter_avatar_proxy/v4/letter/d/a5b964/32.png) [@danhobbs](https://www.ruby-forum.com/u/danhobbs)\
**Post date:** [March 14, 2006, 11:17pm UTC](https://www.ruby-forum.com/t/two-customer-types-best-design-principle/54841/5 "2006-03-14T23:17:52Z")

</div>

I think you want to check out some of the writings on Single Table  
Inheritance

- [Peak Obsession](http://wiki.rubyonrails.com/rails/pages/SingleTableInheritance)
- [P of EAA: Single Table Inheritance](http://www.martinfowler.com/eaaCatalog/singleTableInheritance.html)

Rails supports STI – check out ActiveRecord::Base under STI…
