If you haven't seen The 15 minute E-Commerce Site CLICK HERE
I'm currently looking for Contract jobs, Contact Me if you are interested. Dave.
One of the first things I notice when I look through other people's code is the lack of organization within the controllers. Almost universally I see a structure like this:
- Admin (namespace)
Thus if you create a UI for searching products you would make a "search" action in the productscontroller. This would work but eventually you will have 50 actions in the productscontroller and your development workflow now changes from "open the controller and look at the CRUD method you want to modify" to "open the controller and do a grep for the method that concerns you". Also having a lot of methods in your controller now makes it almost impossible to have a before_filter with passing :except & :only.
A Better Approach
Lets move the example to something a bit more complex. Lets try a checkout process in an e-commerce application. I've seen many approaches to this solution. Most involve a checkouts_controller with about 20 methods within the controller.
Instead lets start with a "checkouts" namespace. Notice the namespace is plural. I namespace using plural because I know the namespace will not share a name with a model I have. For Example: If you have a "user" namespace instead of a "users" namespace and you do User.find, you will have an error.
Moving on with the checkouts namespace the first thing I will do is create a checkouts/base_controller.rb
class Checkouts::BaseController < ApplicationController end
Now you can start creating some of the controllers within the Checkouts namespace.
class Checkouts::AddressesController < Checkouts::BaseController #... end # & class Checkouts::ShippingMethodController < Checkouts::BaseController #... end
So the first thing I would add for the base_controller is a line the requires a user to be logged in like this:
class Checkouts::BaseController < ApplicationController before_filter :require_user end class ApplicationController def require_user redirect_to login_url and return if logged_out? end def logged_out? !current_user end end
What you really want is a method to ensure a user needs to login within 20 minutes of starting the checkout process. For this I would create a method that ensures that after 20 minutes of inactivity the user is required to login again. The following show some of the code that you would need for this.
class Checkouts::BaseController < ApplicationController before_filter :require_secure def require_secure redirect_to login_url and return if not_secure? end # at login session[:authenticated_at] is set to Time.now def not_secure? !current_user || session[:authenticated_at].nil? || (Time.now - session[:authenticated_at] > (60 * 20) ) end end
For another approach and more details of a checkout process take a look at ror_ecommerce's checkout.
The reality is namespaces are a great way to organize code for any application. It keeps your controllers small and it also keeps related controllers in the same folder in your app directory. Also don't be afraid to have namespaces that are several layer deep. For Example Admin::Shopping::OrdersController might be a controller that a customer service representative uses to make orders through the phone.
More great places for a namespace are:
- Shopping (adding things to your cart)
- Site (like "about us" and "jobs")
Also remember a namespace is mainly for organization. If you don't want the end user to see something like http://www.mysite.com/site/jobs and you just want to show http://www.mysite.com/jobs, you can always go into the routes file and make changes accordingly and still take advantage of the organization namespaces give you.
Thanks for reading, I hope this helps.