Restful Shopping Cart Example?

Hi --

I'm still trying to make restful desing click in my head. Yesterday I asked about <a href="http://www.ruby-forum.com/topic/101827&quot;&gt;good examples apps to learn from</a>, and there don't appear to be very many. And most of the examples that do exist seem to be fairly strait forward mappings to rest concepts... posts and comments and such.

So I'd like to see some more examples, and in particular I think it would be fun to turn the "Depot" store app from AWDR into a more restful example. For me it would be a nice example because fairly simple and clean and it's not quite clear to me how a restful desing would be added.

Two sets of questsion to get me started.

1. Does this seems like a useful thing to do? Would anyone learning restful design out there like to see this, and for people who already get it is the Depot app a good candidate?

I think it could be quite worthwhile.

2. And second does anyone have design tips that I should use to get started? So far this is what I've done.

- used the restful_authentication plugin to generate an authentication system. - used scaffold_resource to generate resources to model the store:

  - products   - carts   - cart_items   - orders   - order_items

I'd recommend trying a slightly different approach. (I don't use scaffolding at all, but that's not my focus here.) In my book, I develop a store application, and one of the things I realized early on was that I didn't want or need a shopping cart model. The shopping cart was really a virtual thing; it was essentially a view of a collection of orders.

I've thought about what I'd do if I were to rewrite that very application to use the new REST support. I would still not have a cart model. But I would have a cart controller, and I would do:

  map.resources :cart

(or some nested variant thereof). In my original application, I had actions like customer#view_cart. That could be turned into cart#show.

It's a good example of a case where the whole controller/model stack is probably too much. The idea of a shopping cart resource need not be wired to a shopping cart model. You can still leverage the full range of named routes and abstraction that the REST techniques give you.

Anyway -- it's something to think about.

David

Hey,

comments below:

2. And second does anyone have design tips that I should use to get started? So far this is what I've done.

- used the restful_authentication plugin to generate an
authentication system. - used scaffold_resource to generate resources to model the store:

  - products   - carts   - cart_items   - orders   - order_items

I'd recommend trying a slightly different approach. (I don't use scaffolding at all, but that's not my focus here.) In my book, I develop a store application, and one of the things I realized early on was that I didn't want or need a shopping cart model. The shopping cart was really a virtual thing; it was essentially a view of a collection of orders.

I've thought about what I'd do if I were to rewrite that very application to use the new REST support. I would still not have a cart model. But I would have a cart controller, and I would do:

  map.resources :cart

This brings up an interesting point: when to use map.resource vs
map.resources

Unless you're planning some sort of interface to view *all* carts,
you probably want a singleton resource:

map.resource :cart

Then adding and removing cart items would be the responsibility of
the cart_items controller:

map.resource :cart do |cart|    cart.resources :cart_items end

And with that I wouldn't bother having a cart_items_controller#index,
I'd just use cart_controller#show to present the listing.

Just another thing to think about.

Trevor

Hi --

Hi --

> I don't think I'd have cart_items, though; I'd just have orders. The > cart would be implemented as the aggregate of the orders. So an order > would not have any need to know that it was in a cart, and the cart > would only exist as a particular way of representing the orders.

I was wondering about this approach earlier.

There does seem to be lots of duplication between carts, cart_items and orders, order_items, it would be nice to get rid of that. I've also found and downloaded your r4rmusic example... nice and short... I like that!

To make this work the one trick is that I'll need some way to mark one order as "in cart". I guess I can do this by having a new value for the status field, and also by storing that order id in the current session.

I think I can even present this order to the user as the shopping cart by creating a new singleton root and controller named cart:

  map.resources :cart

Do you mean map.resource? (See Trevor's earlier comment on my earlier post.)

That singleton will actually be backed by the current "in cart" order. Does that all seem to be right?

If an order has a status (pending, confirmed, paid, cancelled, maybe others), then you could just have the cart be all of the pending orders of a particular user, or however you wanted to define it. That way, the order objects don't need to have any knowledge of a cart. "Shopping cart" would just be a colorful word with which to explain to the customer what they were looking at.

David

Hi --

>> map.resources :cart > > Do you mean map.resource? (See Trevor's earlier comment on my earlier > post.)

Yes, that was a typo on my part.

I've not got a first try implementation up for people to look and and more important improve. You can download it here, I'll put it in svn later:

http://www.hogbaysoftware.com/files/restfulstore.zip

It uses an SQLite database. Once you migrate you should be able to go to /store, add items to you cart, and checkout. To access the admin sections the username is 'admin' and the password is 'password'. Please look through the code and help me get this right so that it can be a good example for others. I'm sure there are many ruby and railsisms that I've missed, and maybe bigger design issues as well. Also I've added a number of "HELP" comments throughout the code in places where even I could tell i was probably going down the wrong path.

Here's a quick overview of the apps structure:

1. The model objects stored in the database are user, product, order, order_item.

2. The apps routes are:

  map.resources :users, :sessions

  map.resource :store do |store|     store.resources :products     store.resource :cart, :member => { :checkout => :post, :submit => :post, :completed => :get, :error => :get } do |cart|       cart.resources :cart_items     end     store.resources :orders do |order|       order.resources :order_items     end   end

3. And here is a quick overview of the important paths and their intent:

/store - list of products along with contents of cart /store/cart - singleton resources of the current (per session) pending order /store/cart/cart_items - supports create and destroy actions for adding items to cart /store/products - admin section for products /store/orders - admin section for orders

I'm trying to use the basic design as discussed above. The users cart is really just a special order who's status is pending. I'm using a singlton cart controller to represent that pending order. I've also given that cart controller cart_items which are targeted by actions that add or remove items from the cart.

4. Last the view part of the app is still a bit hacky. Probably many of the admin views can be deleted, and I'd also like to make manipulating the cart a bit more ajaxy, but I was having trouble making that work. So for now it's all quite basic.

Thanks for any comment and suggestions.

I haven't downloaded the app yet -- just responding to what's above -- but just for fun, here's my minimalist version. It may be too minimalist... but it might be interesting to identify things you can do with yours that you can't do with mine.

This is all untested... just hypothetical code and routes.

class Order   belongs_to :user   belongs_to :product end

class User   has_many :orders end

class Product   has_many :orders   has_many :buyers, :through => :orders, :class_name => "User", :source => "user" end

map.resources :products

map.resources :users do |u|   u.resources :orders end

Show all available products:

  GET /products

Show a user all their orders ("cart"):

  GET /users/1/orders

Add a product to cart:

  POST /users/1/orders

Remove a product from cart:

  DELETE /users/1/orders/1

etc.

David

Hi --

David,

Thanks again.

Here are a few things that I think are different, though I might also just not be understanding some parts.

1. In my model 'user' is really just for administrators. I don't want visitors to the site to have to create an account before making an order. For big sites, like amazon, I think that requirement can make sense, but for a small site like I'm imagining I don't want the user to have to decide on a user name and password in the middle of the order process. That does mean that a user can't use the site to see all of there orders, but instead I can add an interface where they enter their email and are sent an email listing all of there orders.

2. You've removed the order_items model, which I think is important to keep if we want visitors to be able to buy more then one product per checkout. With the current design I'm not sure how your "cart" can hold more then one item...

...Or maybe in your model a visitor can have multiple orders pending and all pending orders are considered to be in the "cart". But the problem with this approach is that I don't think you have a good place to store checkout info such as name, address, email, etc. I think you would have to duplicate that info across each order per checkout?

No, I'm assuming it would be stored in the user object. I haven't drafted the database tables, but it would be in there (or a separate contacts table, or whatever).

In my model there is a single pending Order per visitor which is the "cart" and it holds multiple order items, each of which is associated with a specific product and also has a quantity. In my mind the addition of the order_items class makes the model work a bit better since it's strait forward to order multiple copies of the same product and there is a single place to store checkout info.

The Order model could have a quantity field :slight_smile: We're using 'order' differently, though. I'm thinking of an order as per user/per product, and the "cart" as a view of the user's pending orders.

3. In your routes I'm not sure where the admin interface for editing products and viewing orders is. I guess the interface for editing and buying products could both take place at GET /products, but in my model I've instead created the singleton GET /store route/controller that list the products along with BUY buttons and I have a separate GET /store/products route that display the admin interface for editing the existing product collection.

I think distinguishing between /store and /store/products, with one being public and one being admin, is a bit obscure. Could you just have /store/products routes (or even just /products?) and just control access to the operations that change them?

David

We need someone to create a Ruby shopping cart for us or maybe slice our psd file into Substruct. In a perfect world, creating one for us is the key.

We also need this to be syndicated into Atom or RSS 2.0 or both such as the G2 Image Gallery Application. We also are wanting to import an existing WordPress MySQL Database into it. More like a Ruby blog/shopping cart that is syndicated with Atom & RSS 2.0. We also need permalinks or URL friendly concepts, however you want to say it now days.

We also have an existing G2 MySql database that we want in Ruby too.

Can anyone help me? Thanks, sb :slight_smile:

~~@~~@~~

oren wrote:

The Depot application is the perfect example of a good sample REST application in my opinion. I really wish that the Pragmatic guys would release a AWDR v 3 with a fully RESTified Depot application. Just wanted to put it out there that I agree that this would be a good "real" rest application.

I put a little thought into how I would do it and I think the only two database-backed resources would need to be user, order, and product. So those three would have their controllers and then all other controllers would be based on those resources and would use the session.

Now I am tempted to try to whip together an exact copy of the Depot application using all the new Rails 2 principles...

(Looking through the thread, it appears David beat me to the user / order / product resource idea but I will post this anyways)