Hi, I'm using "Agile Web Dev. w/Rails", which is great but has a minimum on grouping controllers into modules. I've successfully generated a module scaffold, so I have: app/controllers/admin_controller.rb app/controllers/admin/things_controller.rb (And the rest of the scaffold output, which seems normal.) However, browsing to http://localhost:3000/admin/things/ gives me "Unknown action No action responded to things". I'm not sure what or how much code I should post to illustrate the situation, or if this is some sort of configuration issue in Radrails/InstantRails. Thanks in advance for any help!
on 2005-12-29 21:53
on 2005-12-30 00:56
I think the problem here has to do with your routes (config/routes.rb). By default the following route is used to handle incoming requests: map.connect '/:controller/:action/:id' In your case, your admin_controller.rb perks up its ears because the first part of the URL (:controller) is 'admin', in reference to it. The controller is now expecting to get and action and optionally and id passed to it but, your URL implies something a bit different that it is unaware of. You are trying to reference a controller inside a module of the same name 'admin'. At this point your admin_controller.rb throws up its hands because it doesn't have a method named 'things' and doesn't know what to do. How is this fixed? I am no expert but here is what I would do: 1. Rename your admin_controller.rb 2. Create a custom route in routes.rb for your 'admin' module map.create '/admin/:controller/:action/:id'
on 2005-12-30 10:01
Jeremy M. wrote: > > How is this fixed? I am no expert but here is what I would do: > > 1. Rename your admin_controller.rb > > 2. Create a custom route in routes.rb for your 'admin' module > > map.create '/admin/:controller/:action/:id' Thank you so much for taking the time to reply. I tried suggestion #2 first, since that's so easy; it didn't have any effect. I'm not sure I understand #1... If I rename the top-level controller then my module won't be inside it any more, which I thought was the point of modules. As I understand it, when an incoming request has a controller named "admin/things", Rails looks for a controller called "things_controller" in the directory app/controllers/admin. Doesn't this also mean that I want to have a top-level controller called "admin"? Thanks again,
on 2005-12-30 19:20
> If I rename the top-level controller then my module won't be inside it > any more, which I thought was the point of modules. By 'top-level controller' do you mean app/controllers/admin_controller.rb? This controller is not in a module. The only time a controller is in a module is when it is in a sub-directory of the base controller directory (app/controllers/<module>/some_controller.rb). Conversely, any controller NOT in a sub-directory is NOT in a module. I hope I am not confusing it more. > As I understand it, when an incoming request has a controller named > "admin/things", Rails looks for a controller called "things_controller" > in the directory app/controllers/admin. By default no, this is not what Rails does. When an incoming request has a controller named "admin/things" how does rails know which part of the URL is referencing the 'controller' and which part is referencing the 'action'? Short answer. It looks at config/routes.rb. Long answer. Remember we are talking about default Rails behavior here, not a modified route as was suggested in my previous post. Ok, so the default route that Rails uses is: map.connect '/:controller/:action/:id' This in basically says, for each incoming request, I understand the URL to be implying the following: o The string (if there is one) in the spot labeled 'controller' is what I should use as the controller. o The string (if there is one) in the spot labeled 'action' is what I should use as the action. o The string (if there is one) in the spot labeled 'id' is what I should use as the id. That means, if you pass it a URL that is in the form of 'admin/things' it will do the following: o Assume the controller is 'admin' since the string in the controller spot is 'admin'. o Assume the action is 'things' since the string in the action spot is 'things'. o Assume there is no id since one is not specified. At this point, Rails will try to find a controller named admin_controller.rb in app/controllers/admin_controller.rb and execute the action (method) 'things'. However, in your setup, it finds the controller it is looking for but there is no method named 'things'. This is why you get 'Unknown action No action responded to things'. > Doesn't this also mean that I want to have a top-level controller > called "admin"? No. In fact, you probably don't even need app/controllers/admin_controller.rb if I understand what you are trying to do. If I were you, I would get the 'Agile Web D. with Rails'. There are entire chapters that explain this stuff. It really helped me to understand it more clearly. http://www.pragmaticprogrammer.com/titles/rails/index.html
on 2005-12-30 20:31
Thanks again, Jeremy. I appreciate your help with this. I get what you're saying about routes, and how Rails interprets '/:controller/:action/:id' by default. The rest of my app is working well according to those rules. As it happens, I am using "Agile Web D. w/Rails" -- specifically I'm looking at page 191, which says: "So far, all our controllers have lived in the app/controllers directory. It is sometimes convenient to add more structure to this arrangement. For example, our store might end up with a number of controllers performing related but disjoint [sic] administration functions. Rather than pollute the top-level namespace with each of these, we might choose to group them into a single admin namespace. Rails does this by using a simple convention. If an incoming request has a controller named (say) admin/book, Rails will look for a controller called book_controller in the directory app/controllers/admin. ..." There's not much more; it sounds like you have a copy to refer to. So, I apologize if I'm being difficult, but can you help me understand page 191, versus your explanation of routes here? Thanks again,
on 2005-12-30 20:48
> So, I apologize if I'm being difficult, but can you help me understand > page 191, versus your explanation of routes here? Thanks again, Certainly. It appears I have a bit more reading to do to understand what is being said myself. I'll try and answer if no one else does in the mean time. :)
on 2005-12-31 19:15
I did some experimenting, and it seems to work like this. I was trying to "mix" a top-level controller with a module, like this: app/controllers/admin_controller.rb app/controllers/admin/things_controller.rb ...where admin_controller contains some methods like "index", "login", etc. My impression was that for each incoming request, Rails would look in the top-level controller, not find "things", and then look for a module. This is what didn't work. After experimenting, it looks like maybe you're not supposed to "mix" controllers like that. I removed admin_controller, and moved all of the methods in it to a second module -- so now it looks like this: app/controllers/admin/login_controller.rb app/controllers/admin/things_controller.rb So now these URLs work (with default routes): http://localhost/admin/login/login http://localhost/admin/things/list The remaining issue is that this URL doesn't work: http://localhost/admin/ ...because there can't be a top-level controller for that. I am getting around this by installing a custom route, like so: map.connect 'admin/', :controller => 'admin/login', :action => 'login' So my app is working in a coherent manner, which is good. I'm not confident, because this is really not covered in the Agile book. Can anyone confirm that this is how modules in Rails are *supposed* to work?
on 2005-12-31 20:53
I would say yes, that is the way to program module controllers like that. I have mine setup up the same way for the admin modules and have never and any problems with it. I would say keep going the way you are. -Nick
on 2006-01-23 11:16
hi, Another idea: create your module with a controller inside it named 'BaseController'. admin/base_controller.rb class Admin::BaseController < ApplicationController Then all of the other admin module controllers should be children of 'BaseController'. admin/thing_controller.rb class Admin::ThingController < Admin::BaseController I think this is the kind of inheritance you were after. All controllers in the module inherit all the functionality of BaseController.