Restful Shopping Cart Example?

I’m still trying to make restful desing click in my head. Yesterday I
asked about good
examples apps to learn from
, 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?

  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
  • use the resources_controller plugin to simplify the controller
    implementation for the apps resources.

  • right now these are what my routes look like:

map.resources :users, :sessions, :products

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

map.resources :orders do |order|
order.resources :order_items
end

So now I can browse and edit the model layer of my store, but the view
and controller layers are missing a lot and I’m not sure where to go
next. Whats the proper way to add the basic Depot style behavior ontop
of this structure. The features that I’m missing are:

  • list of products with “Add to Cart” buttons next to each.
  • shopping cart on each page showing what I’ve added.
  • empty cart behavior
  • checkout behavior that creates a new order

I know how to get all of this behavior by copying the Depot example from
the book and creating a StoreController, but it seems like that’s not
the most restful design. So please helpe point me in the right direction
and maybe I’ll be able to get a good example going.

Thanks,
Jesse

Hey,

comments below:

On 20-Mar-07, at 10:51 AM, David A. Black wrote:

  • cart_items

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 –

On 3/20/07, Jesse G. [email protected] wrote:

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.

  • 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


Q. What is THE Ruby book for Rails developers?
A. RUBY FOR RAILS by David A. Black (http://www.manning.com/black)
(See what readers are saying! http://www.rubypal.com/r4rrevs.pdf)
Q. Where can I get Ruby/Rails on-site training, consulting, coaching?
A. Ruby Power and Light, LLC (http://www.rubypal.com)

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

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

Also are there any other tradeoffs related to getting rid of the carts,
cart_items models that I’m not thinking off? Depot example does use a
cart model of sorts as do a few other store example that I came across.
Am I giving anything up by getting rid of the cart, cart_items
abstraction?

Thanks,
Jesse

Hi –

On 3/20/07, Jesse G. [email protected] wrote:

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


Q. What is THE Ruby book for Rails developers?
A. RUBY FOR RAILS by David A. Black (http://www.manning.com/black)
(See what readers are saying! http://www.rubypal.com/r4rrevs.pdf)
Q. Where can I get Ruby/Rails on-site training, consulting, coaching?
A. Ruby Power and Light, LLC (http://www.rubypal.com)

Hi –

On 3/20/07, Trevor S. [email protected] wrote:

  • used the restful_authentication plugin to generate an

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

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.

David


Q. What is THE Ruby book for Rails developers?
A. RUBY FOR RAILS by David A. Black (http://www.manning.com/black)
(See what readers are saying! http://www.rubypal.com/r4rrevs.pdf)
Q. Where can I get Ruby/Rails on-site training, consulting, coaching?
A. Ruby Power and Light, LLC (http://www.rubypal.com)

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

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

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

Hi –

On 3/21/07, Jesse G. [email protected] wrote:

later:
could tell i was probably going down the wrong path.
map.resource :store do |store|
3. And here is a quick overview of the important paths and their intent:
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.

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


Q. What is THE Ruby book for Rails developers?
A. RUBY FOR RAILS by David A. Black (http://www.manning.com/black)
(See what readers are saying! http://www.rubypal.com/r4rrevs.pdf)
Q. Where can I get Ruby/Rails on-site training, consulting, coaching?
A. Ruby Power and Light, LLC (http://www.rubypal.com)

Hi –

On 3/22/07, Jesse G. [email protected] wrote:

order. For big sites, like amazon, I think that requirement can make

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

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


Q. What is THE Ruby book for Rails developers?
A. RUBY FOR RAILS by David A. Black (http://www.manning.com/black)
(See what readers are saying! http://www.rubypal.com/r4rrevs.pdf)
Q. Where can I get Ruby/Rails on-site training, consulting, coaching?
A. Ruby Power and Light, LLC (http://www.rubypal.com)

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

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.

Ok I think I see what you are doing. I think in my design Order is a lot
like the User in your design. And the OrderItem in my design is like
your Order. In your design does a visitor to your site require a login
to get a User object? Anyway I think I’m pretty comftorable with this
difference and I don’t think it has a big impact on the restful aspect
of the design.

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?

It still seems to me like it would be good to have them separate. For
example in the “store” interface I want to show “Add to Cart” buttons
and I don’t want the site visitors to see any create, edit, or destroy
links. While in the admin list I don’t want to show the “Add to Cart”
button, and I do want to show those links. So I think there needs to be
two different views, but maybe it would be better modeled by adding a
custom action to the products resource? For example:

GET /products - shows the public accessible storefront with buy buttons
GET /products;admin - shows the admin interface

Do people think that’s a better design then using a separate store
controller to present a public view on products?

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?

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.

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

Thanks,
Jesse

Hi Jesse,
I want to develop a rest-ful shopping cart as well.
Any updates regarding your implementation?

Oren

I still have one big restful question about making the Depot app design
restful, with a possible answer at the end.

I’m still really not sure on the best way to do the “add to cart”
action. Forgetting the full store design discussed earlier, lets pretend
that I’m just trying to model that single operation. To make this simple
lets imagine that I’m copying part of the Depot app design and I’m just
dealing with two objects, Cart and CartItem. A cart holds items, and
each item has a quantity and a string naming a particular product.

class Cart
has_many :cart_items
end

class CartItem
belongs_to :cart
attr_accessor :quantity
attr_accessor :product_name
end

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

With that design it seems like to add a new item to a cart you would
use:

POST /cart/cart_items

But the problem here is that items have a quantity, and that means that
you really only want to be done a POST the first time you add a specific
project, after that you’ll just want to update the items quantity and
should be using PUT. And figuring out how to choose between those two
actions has always been confusing to me…

Is it the case that with the current Depot model design adding an item
to a cart can’t really be done in a completely restful manner? I’m
starting to think that might be the case, and that the solution is to
get rid of the quantity attribute from cart item. Instead if two of the
same product is added to a cart then two different CartItems should be
created? Does that seem like a better design? It would make it easier to
do things restfully…

Thanks for any ideas,
Jesse

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:

Hi Jesse,
I want to develop a rest-ful shopping cart as well.
Any updates regarding your implementation?

Oren

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)