Beginner's routing question: Redirecting to a different controller

I have a problem passing control from one controller to the next. Here are the details:

I have a model 'Dict' with primary key :dictname. I have two controllers, named 'Login' and 'Dict'. The application starts in views/login/index.html.erb, where I have - among other stuff - an entry field for the dictname. When clicking a button, control passes to a method in login_controller.rb, where various authentification is being performed.

If all checks pass, control should now be transfered to a page views/dict/manage.html.erb , and this page should receive the dictname as a parameter.

Here is what I have tried so far:

In routes.rb I placed an entry

    get 'dict/:id/manage', to: 'dict#manage'

In login_controller.rb, I tried to transfer the controll with

    redirect_to dict_path(@dict)

However, I get the error message

    'undefined method dict_path'

I thought that dict_path should be a helper method, which is generated out of my routes.rb . Since this method is undefined, I suspect that my routes.rb is not correct.

Note that I did NOT place a

    resources :dicts

into routes.rb, because - for the time being - I don't need yet the full set of CRUD capabilities on a Dict, so I thought I'll just start with the minimum needed, and extend over the time as necessary. If you think that this is an unwise decision, please let me know.

Starting with the minimum you need now and building up is a good idea. BUT you can go ahead and put the resource in the routes if you want. The result of this is that an attempt to access an unimplemented action will simply give you a different error message. (Or you can go ahead and stub in a simple action that just renders a better error.)

get ‘dict/:id/manage’, to: ‘dict#manage’ , as: :dict

I agree with everything Scott says in his post. This is a small point, but I don’t like having routes defined that aren’t really used. It’s a small point, because my solution is also going to lead to a different error, but I prefer to head things off at the source. I would use resources, as Scott suggests, but limit the actions that you really need such as:

resources :dicts, only: [:show]

resources :dicts, only: [:show, :index]

you get the idea. you can then relax the restrictions as you build out the functionality.

Very good point. My thinking was colored by the fact that I really only do this during development, where I expect to implement the rest of the actions very soon.

Евгений Шурмин wrote in post #1146886:

get 'dict/:id/manage', to: 'dict#manage' , as: :dict

Could you please kindly explain, what the effect of the "as: :dict" is in this case? I understood that I need the "as:" parameter for those cases where I want to name the route differently, but in this case, it is always 'dict'.

With other words: What would be the effect if I just leave out "as: :dict"?

mike2r wrote in post #1146887:

I would use resources, as Scott suggests, but limit the actions that you really need such as:

resources :dicts, only: [:show]

Now in my case, the action has a "non-standard" name, i.e. ":manage".

Can I use this too in the "only:" array, or should I instead use a standard action (in this case probably "edit"), which then, inside my controller, invokes the "manage" method?

routes.rb defines which controller methods are invoked, when a certain request arrives, and the "resources" definition just creates standard routes with standard action names for the "common case" (show/index/edit/....). Did I grasp this correctly?

If I understood this right, I would have two possibilities: Use ab action name which reflects the purpose of the action (here: 'manage') and write a special route definition, as Евгений Шурмин suggested above, or use the standard action names, and use a 'resources' definition. From a viewpoint of maintainability, what would you consider the better solution?

To answer your first question, when a segment key is part of your route (:id) you need to tell rails what you want the named route to look like or it won’t generate one. Even if you didn’t have the segment key, for example:

get ‘dict/manage’ => ‘dict#manage’

it would generate the named routes dict_manage_path and dict_manage_url which isn’t what you want. Therefore, you need the as: :dict to generate the named route you are after.

In answer to your second question, yes, you have grasped the basics correctly and yes. The resources statement creates the standard routes as well as the named routes to go with them. However, it’s part of a larger architecture of handling REST transactions. When you use the following:

rails generate scaffold dict

it will generate not only the routes statement, but the standard actions and views to go with it. If you stick to the rails convention, it will save you a lot of time and work, and I advise that you do so unless there is functionality that just won’t work with that structure.

I would argue that from your description (i’m at a little bit of a loss since I haven’t actually seen your controller code), dict is a resource, even if you don’t intend to initially offer users full CRUD options on the resource. Therefore, I prefer setting this up in the routes (and elsewhere) as a resource and using standard Rails action names. The word “manage” to me could be adding a new entry to dict, modifying an existing entry, etc. This actually involves a number of actions and it would be better to more specific about the purpose of each action. By the way, edit actually involves two actions. The first is to provide the user with a form populated with the values of the existing entry to be edited. The second happens when the user makes changes and submits the form, and the resource is updated. In the Rails architecture, these are the edit and update actions respectively and you would need to allow both in your resources statement.

If you don’t like the Rails standard names for your routes, you can change them, although I still maintain it’s better to learn the Rails way of doing things and stick to it when possible. This makes maintenance and integration with other parts of your application much easier. However, in this case, let’s assume we want to say manage instead of edit. You would route as follows:

resources: :dicts, path_names: { edit: ‘manage’ }

Note: this changes the path name, so you will have /dicts/:id/manage, but it doesn’t change the controller actions, it will still map to the action edit in the controller. Usually this is only done when there are specific reasons to do so, such as creating a site in a different language where you want the URL’s to reflect a language other than English.

Thanks a lot for the detailed explanations. Two remarks for this:

As for the naming, my application evolves a dictionary of foreign language idioms, and the main purpose it to train the user in the usage of these idioms. From the perspective of the user, there are 3 types of "screens" (HTML pages):

- The login page (where he identifies himself, chooses (or create) a dictionary)

- The Management page (where he can import/export dictionaries to/from database, merge other dictionaries into the existing ones, add new idioms (this is really an "edit" action) and, most important of all, select one of several training plans and start trainig).

- The Training page, where an idiom training is performed on the fly. This page also allows, for convenience, editing vocabulary on the fly (but only the one which is presented right now).

From this setup, I found that the usual CRUD actions don't go that well with the application, and that's why I invented the action "manage" in dict, and in the meanwhile the action "start" in the (new) training_controller. However I am flexible with names, and if an experienced Rails developer strongly suggests going with standard action names, I certainly don't object. After all, I can still keep the "meat" of my code in my "manage" routine, and just have the "edit" method call "manage()".

As for "generate scaffold", I have used this when doing exercises back with good old Rails 1, but now with Rails 4, the tutorial doesn't use "generate scaffold" anymore, but always "generate model" and "generate controller". Do you recommend me to investigate into "generate scaffold" too?

Thanks a lot for your detailed comment. It really helps a lot!

One final question: I have read various tutorials, but when it comes to look up the API, I found the documentation a bit confusing, and difficult to find what I am looking for. You gave for example a suggestion to use the "resources" method with the path_names: parameter. Though I have stumbled several times over the usage of 'resources', I haven't found a single place, which would explain the usage of all parameters (such as path_names:) and examples of usage. Right now, when I really want to know how something is done in Rails, I google for it, hoping that someone else had the same problem. This often works out, but I would be more happy if I had a documentation of the Rails classes and methods, in a similar way as it is for Ruby and the standard functions and -classes. Is this available, or is Rails in the flux so much that we can't expect this yet?

OK, this gives me some more information, but I’m still not completely sure I understand the application. It sounds like a given user can have one (or more?) dictionaries associated with the user and that each dictionary has one or (probably) many idioms associated with it. In this case, dict is a resource. If I read this right, in your initial application a user could create a dictionary. I’m assuming that you may, in later development, have the option to edit a dictionary (which is different from editing an idiom), or delete a dictionary. However, you also have elements such as import and export that are not standard CRUD actions. You’ve included them in an action manage, but I have a feeling that import and export may be also be separate actions. I’m making some assumptions here which I frequently have to do when answering posts on this list, and if I’ve assumed incorrectly, that will affect my answer.

In that case, you would need to extend the resource to include actions in addition to the standard new, create, edit, update, show, index, and destroy actions. In this case, let’s assume we want to add the action manage. You would do that as follows:

resources :dicts do

member do

get :manage

end

end

This differs from path_names. path_names changes the name of the path but does not otherwise affect the routing or actions associated with the resource. The above code adds manage as a separate action, and creates the appropriate routing and named paths to go with it.

With respect to the scaffold generator, yes, I believe it is worth using, but there are developers that would disagree and don’t use it. Some of the tutorials, such as the railstutorial.org tutorial that is commonly referenced on this list, have you use the controller generator and model generator so that you learn the concepts involved by being forced to manually create a lot of the elements. I believe this is appropriate and a good approach for a tutorial. Likewise, the same tutorial takes you through the steps of manually creating a user resource and authentication. Again, you should, IMO, understand these concepts. However, there are are some ways to make your development more efficient once you have mastered these concepts. The scaffold is one of them. Typically, I start with a scaffold and generally modify it to fit the particular needs of that resource. Likewise, I don’t usually (never) roll my own authentication, I use the Devise gem.

With respect to documentation, it is true, Rails changes quickly (IMO, mostly for the better) and sometimes the documentation will lag a bit. I believe, in general, the Edge Guides are well done and would recommend them. There are a few cases where they may not be as comprehensive as you might need. There is one other resource I would recommend. The Rails 4 Way is a book you can buy from leanpub.com. I don’t have any connection to the book or that site, but if you buy it from them, you automatically get any updates to the book and they update fairly regularly. This is, IMO, an excellent reference. I would not use it to learn Rails basics. It’s fairly advanced and would not be appropriate until you have mastered the basics through a tutorial, but it covers the topics you asked about better than any other resource I know of. Hopefully, others have some input here as I’m sure there are resources I’m not aware of. The Railscasts are also good, but I’m not sure if they’re being maintained and updated at the moment.

The classes and methods themselves are documented in the Rails API (api.rubyonrails.org). For example, at api.rubyonrails.org/classes/ActionView/Helpers/Urlhelper.html you will see all the methods and options for button_to, link_to, mail_to, etc. that you use in view rendering.

You did grasp the concept of my application amazingly well, only that the only possible ways to "edit" a dict is to add and remove idioms (would you see this as editing the dictionary, or as manipulating the Idiom class?).

The reason why I did not follow the CRUD way - and maybe this I was mislead here - was the following:

When starting the design, I started with a concept of the screens the user is going to see. The initial screen will present an entry form for the name of the dictionary, the user name, buttons for creating and opening a dictionary, and - for the creation case - an entry field denoting a dictionary type (think about it as the "language" for the dictionary, but the concept is more general).

After clicking one of the buttons, a new screen appears from where the user can click various buttons - exporting the dictionary, start one of three predefined training types, manually adding idioms to the dictionary (but on this screen no possibility of deleting or editing idioms, nor for the deletion of the dictionary).

My idea was that the logic for each web page should correspond to one controller method. In this case, I have used two different controllers: For the entry page I have a login_controller (because I would like to add password authentification later on), and for the second form I have a dict_controller.

Similarily, I am planning a training_controller for doing the idiom training, and another controller (maybe idiom_controller) for adding, deleting and editing individual idioms.

Maybe this mapping "web page" to "controller" is, from the onset, bad design?

Web pages would be more likely to map to a controller action, not to a controller. It’s common to start by laying out your web pages and then working back from there. However, I’ll warn you it’s usually a back and forth process. Once you have the web pages laid out, you need to establish a structure that usually involves grouping pages to controllers that follow Rails conventions.

In this case, you have at least three resources: user, dict, and idiom. You ultimately will most likely have more. I would recommend you go through the tutorial at railstutorial.org because it does a very good job of introducing these concepts and you will really need to understand this when you introduce password authentication.

Editing a dict resource would not be adding and removing idioms, that would be the create and destroy actions of the idiom resource. Editing a dict resource would involve renaming the dictionary or changing the dictionary type.

Rails comes with a default set of actions related to a resource but you don’t have to use all of them. It’s also not a perfect science. A good example is your training screens. That might be its own resource, it might just be an extension of the idiom resource, or it could be its own controller but not correspond to a resource. It depends on how much functionality there is. Not everything will fit into a resource. For example, many applications have static pages such as a home page, an “about us” page, etc. I usually group those into a pages controller which stands on its own, it has no resource or model behind it. Again, the tutorial will do a much better job of explaining this.

Thank you for the suggestions. I went through the tutorial found on the web (which was explaining the concepts using the example of making a blog site), but could relate what I have learned, only to the concept of the idioms in my project, but not to the rest of my application.

I now see that is a good idea to treat the user as its own resource, because sooner or later I will need this anyway, and I will try to redesign my application.

When you get to the point where you are going to use password authentication, re-read the user authentication part of the tutorial. Right now, you have login mapped to an index action. Actually, login uses the concept of a session resource and you create a session when you log in. You will want to understand that concept and how a session store is used to maintain security.

Good Luck.

mike2r wrote in post #1147433: