fallenrogue.com

Creating a RESTful admin section in Rails.

Ok, so REST is the bomb, everyone is a fan and recently I said why I was in love. If you missed the discussion in the comments, one reader, John, mentioned that he was at his wit’s end trying to figure out how this new method of thinking of resources with a single URL fit in with the concept of a traditional admin section. In the comments I outlined 2 version of how to accomplish this and now I’m going to do the same here. Only I plan to break the 2 methods into 2 articles so this is part 1 and it details the following…

Create an admin section using a single controller yet giving illusion of 2. Enough talk, let’s code! For those who love to follow along without typing you can pick up the project here. For those who like to type-along… let’s go. Remember, I use sqlite3 because it’s pretty disposable. You are welcome to use whatever you want. Also, we’re going to be using Rails 1.2.2 on Ruby 1.8.5 if you’re curious.

rails product_factory -d sqlite3

Ok, so all we’re going to do… I know, it’s so useless but this is more about the concept than anything truly functional… is create a Product! At the moment we’re going to add the user authorization but I’ll show you where it goes just in case. So, let’s get going…
script/generate resource product name:string description:text
rake db:migrate

Ok, so here we’ve created the product resource and we’ve added the schema to the database. Now, let’s crack open the routes.rb file and do some tweeking. If you’d like, you can use the scaffold_resource generator. It does the same thing but also creates the views and stuff for you so you don’t have to add them manually, like I plan to do. :) Either way is fine. Ok, back to routes.rb
  map.resource :admin do |admin| 
    admin.resources :products, :name_prefix => 'admin_' 
  end 
  map.resources :products

ok, so what have we done? We’re really only added a named route for our product resource that allows us to call at it in 2 ways…

/product/
or
/admin/product/
Both will work but the other thing that (:name_prefix=>’admin_’) does is it gives us the URL helper so that we can call out not only…
products_path (et all...)

but
admin_products_path (et all...) :)

as well. And they all point to the same controllers. Since this example assumes that it’s all still one resource and that an authenticated user can edit, update and delete and a regular user can only view, we’re on the right path. So, now that we’ve done some stuff, why not open or create index.rhtml under the products directory and see this in action. Throw this in there, fire up mongrel and open up http://localhost:3000/products.
products_url: <%= link_to products_path,products_path %><br>
admin_products_url: <%= link_to admin_products_path,admin_products_path %>

now, click the links and what do you notice? Nothing! No difference, we’re pointing to the same resource. So, check if they are an authorized user and show them some stuff, right? Sweet, how about a different layout altogether? In the layouts directory, create 2 files “admin.rhtml” and “application.rhtml”. You can dress them up however you like, but all I have done for now, it to throw an H1 element in each telling me where I am and the call to yield. like so…
<h1>Hello regular person</h1>
<%= yield %>

AND
<h1>Hello admin person</h1>
<%= yield %>

and I add this little ditty to my product controller.
  layout proc{ |r| (r.request.request_uri.index(/admin/) != nil) ? "admin" : "application" }

Now, remember, this is NOT what you’d want to do! You’re going to be authenticating users and at this point you’d want to write logic to swap the layout based on the logged in user and not if they have the word admin in the url. This is JUST for proof of concept. Ok, now that you’ve been warned let’s fire open a browser and look at what we’ve got.

You should be able to click the links and see the different (albeit pretty lame…) layouts using the same view and controller. Awesome-tastic or what?

In part 2 we’ll cover the same concept but using 2 controllers! You know, just in case you need it! :) Thanks again to John for having the issue that created the need for this article. :)

As always you can find the project here.

articleStats

Here are some silly little facts about this Creating a RESTful admin section in Rails....

It was written by about 1 year ago.
It has 4964 letters in it.
It has 766 words in it.
It has a total of 15 comments in all.
So far Leon has the last word!

article Links

These are the links that appear in this article. They probably don't make sense out of context... but just in case. :)

project here.
Rails
you can find the project here.
 

The other stuff...

What the kids are saying...

about 1 year ago Chris said...

Sweet.

Curious, which makes more sense -- one controller or two? It seems like you'd be wiring in a lot of conditionals and such to differentiate between the two intended targets in the one-controller version ... maybe I'm off on that thinking.

Oh well, I'll just hang around for that second article that'll prolly clear it all up for me. ;)

about 1 year ago fallenrogue said...

It's a great question, Chris! I'm almost done with part 2 and after that I think we'll open the floor for debate on this issue. :) As of now, I'm leaning towards a "either way works as long as you don't confuse your goals" mindset. The above example, where the layout is determined by a Proc, the logic is easily replaced if you've already got a method like "is_admin?" Granted, it means you may end up with a few more view helper methods or conditionals in your views but your controller will be clean. That's what I'm trying to get to the bottom of in the next article. Is it better to go with 1 or 2? Right now... it depends on the app. :)

about 1 year ago john said...

I went with the 2 diff controller route because the one controller, all it does for product is really list the items. So I figured I'd keep it separate and just have a before filter for the admin section to weed out any unauthorized peeps.
- John

about 1 year ago Jared said...

I'm curious to see how you handle using one controller for a resource that's both nested and not nested at the same time.

I had tried this a while back with Nested Polymorphic Resources (Users and Companies with nested Addresses) and found that I wound up with a lot of ugly conditional code in my views for the link_to helpers:
users_addresses_path(@user), companies_addresses_path(@company), etc.

about 1 year ago fallenrogue said...

Hi Jared! I'm not quite done with the part 2 and it doesn't include poly nested resources... maybe that's part 3! :) Until then, have you tried the SimplyHelpful plugin? Might make life with those poly nested resc easier. :)

http://dev.rubyonrails.com/svn/rails/plugins/simply_helpful/

about 1 year ago sean said...

Is there a way to set 'login_required' if the URI includes /admin ?

Such as a beforefilter.

10 months ago fallenrogue said...

Hi Sean, sorry I never saw the comment so I didn't reply. In the off chance you ever return to this article the answer is yes. Before filters can be used to inspect the url and allow you to redirect if desired. Completely up to you. :)

6 months ago AkitaOnRails said...

I just published a "screencast":http://www.akitaonrails.com/2008/1/25/easy-restful-rails-screencast to show how to make Restful Rails even easier. Take a look.

6 months ago Leon said...

sweet! I'll check it out!

3 months ago gordon said...

any idea how to get this working with caching? my problem is that page caching seems to break the cms because the admin section gets cached as well - it's calling the same actions...

3 months ago Leon said...

@gordon: how so? Same actions for displaying data would be fine, wouldn't it? If it's for edit/create those should invalidate the cache after the sweep right?

Drop some more info and I'll do my best to help! Have you considered fragment caching instead of action caching? Let me know, thx!

3 months ago gordon said...

thanks for your reply!

when i have caching turned on it's caching my front end pages eg stories.html.

if i go to my 'admin' nest of the same page it creates a folder admin/stories.html

so then if i try to do a post new story it gives me an error saying that post is not allowed to an html file....

so i'm assuming i need to find a way to turn off the caching just for the nested...

i thought maybe i could block it by giving that admin folder no read or write acccess but that didn't work (ERRNO EACCES ERROR)

i was thinking maybe action caching could solve it but in my case i think page caching would be ok if i could just turn off the nested caching.

or maybe i don't have the sweepers set up right - perhaps it's sweeping too late or not at all and that's causing the trouble?

3 months ago Leon said...

It's hard to say without seeing it but I would say that you last question (assumption) seems correct. Make sure those sweepers are sweeping. I also don't know why you'd be posting to .html files... doesn't add up either. double check those and also take a peek at fragment caching instead of action caching... that may end up being your best bet.

3 months ago gordon said...

indeed i think it was just a case of incomplete and buggy sweepers.... thank you so much for your advice and for this great article!

3 months ago Leon said...

no prob, gordon! Good luck with your app!

Leave a comment
*name:
*email: (never sold or published.)
url :

©2000-2008 fallenrogue.com | Some Rights reserved.