Scoping made easy

One feature request that has come up recently while working on my e-commerce project was to support multiple storefronts from the same codebase and database, but have different product selections depending on which storefront you were browsing. So, if you browsed, you would see all the products from the database, but if you browsed, you would only see products from specialvendor. That is, of course, if specialvendor had requested that only a sub-set of products be displayed. If that's not the case, then all the products should be available, just as if you had browsed to the main site.

So, I decided to set up a many-to-many relationship between Store and Product, with a before_filter to load the Store by subdomain. If no store matching the requested sub-domain is found, then the default store is used. Aside from the (optional) list of available products, the Store model also has things like the theme to use for the layout, customer support contact email address, and so on.

Finding the products for the store isn't as simple as checking the Store object for a value, though. Every method that returns products needs to be restricted to the list of products for the store, if such a list is present. Enter the Scoped Access plugin. This plugin is a gem, and makes this kind of thing as easy as pie.

First, I set up a before_filter to load my store: [ruby] before_filter :load_store

def load_store @store = Store.find_by_domain(request.subdomains.first) || Store.find(1) end [/ruby]

Then I use the Scoped Access plugin to set up a scope for calls to Product if there is a list of products associated with the store: [ruby] around_filter, :store_products)

def store_products return {} unless @store.products.any? { :find => { :conditions => ["store_id = ?",], :include => :stores } } end [/ruby]

Bam! Just like that, all Product finds get scoped to only find products from the current store, if the store has a product list. If the store doesn't have a product list, no scope is applied, and all the products in the database are available. It doesn't get much easier than that!