When you use rails generate scaffold admin/user --model-name=User or rails generate scaffold_controller --model-name=User it generates almost everything in a namespaced fashion. You get app/controllers/admin/users_controller.rb with your controller and app/views/admin/users/ filled with your views.
The one thing it doesn't get right is your paths. You have to manually go and replace references to user_path with admin_user_path and the like. This is pretty tedious.
Is there a way to tell Rails to generate the paths to point to your new namespace, rather than the namespace that the model is in?
Using Rails 4.
With rails build-in generators you can't.
See the generator source code to understand why:
<td><%%= link_to 'Show', <%= singular_table_name %> %></td>
<td><%%= link_to 'Edit', edit_<%= singular_table_name %>_path(<%= singular_table_name %>) %></td>
<td><%%= link_to 'Destroy', <%= singular_table_name %>, method: :delete, data: { confirm: 'Are you sure?' } %></td>
</tr>
As you can see, it goes with edit_<%= singular_table_name %>_path to generate the edit path, without considering name-spacing. (And haml-rails does the same)
The best thing to do, if you have time and patience for it, would be to fix this on the codebase and propose a PR. That's the main point of open-source after all.
If you go this direction, have a look first at open issues, I haven't dive deep into but it seems that different conversations are going on about that matter. Like https://github.com/rails/rails/pull/13927 or https://github.com/rails/rails/issues/21652
Or you can use existing gems like Beautiful-Scaffold that seem to be supporting namepacing
Related
I am currently building an online course and I was having trouble accessing a lesson that is associated with a certain course.
A developer friend of mine solved the problem for me but I'm not really sure why this code works and if there are different ways, more of a Rails conventional way to write this code.
<% #courses.each do |course| %>
<tr>
<td><%= link_to course.title, "courses/#{course.id}" %></td>
</tr>
<% end %>
I am not sure what this part "courses/#{course.id}" is doing. Is there a way to write this using a more conventional seeming names route helper?
It should be the same as course_path(course)
This call just figure out the path for you. The expression in your code simply build this path putting together "courses/" and the id of the course (but using interpolation, not concatenation).
As Ursus answer explains, courses/#{course.id} creates URL directing to specific course path by using string interpolation. For example, if #courses variable is an array with Course objects with ids: [1, 2, 3], then you will receive links directing to "course/1", "course/2", course/3".
To replace that interpolation, you can simply write
<%= link_to course.title, course %>
It will create the same output as "courses/#{course.id}"
To learn more about string intepolation, you can start here: http://ruby-for-beginners.rubymonstas.org/bonus/string_interpolation.html
For some reason your friend did this:
<td><%= link_to course.title, "courses/#{course.id}" %></td>
...instead of this:
<td><%= link_to course.title, course %></td>
...and I have no idea why. The second example is how you use links in Rails. The first example doesn't safeguard you against possible future URL changes.
I'm going through the Rails "Getting Started" guide and noticed an interesting differentiation in the code for determining a link path.
For the parent model, article, we have:
<%= link_to 'Destroy', article_path(article),
method: :delete, data: { confirm: 'Are you sure?' } %>
And for the child model, comment, we have:
<%= link_to 'Destroy Comment', [comment.article, comment],
method: :delete, data: { confirm: 'Are you sure?' } %>
These seem to be very different formats for a link to a model in similar circumstances. Is there an explanation - technical or methodology-related? I'm especially curious why an array of 2 items is needed for comment.
Lastly, my inheritance instincts tell me the code should be article.comment instead of comment.article. Any reasoning behind this ordering?
Adding to #hashrocket answer, It all comes down to how Rails creates routes for nested resources. If run rake routes you would see this
article DELETE /articles/:id(.:format) articles#destroy
article_comment DELETE /articles/:article_id/comments/:id(.:format) comments#destroy
That means, a delete request for an article just takes :id as key/parameter, whereas the delete request for comment needs two, article_id(the :id of the article that particular comment belongs to) and the :id of the comment itself.
In simple terms, to delete an article, you just need its :id so it is article_path(article), whereas to delete a comment you need the :id(which serves for :article_id) of the article it belongs to and the :id of the comment,so it is [comment.article, comment]. You can also write it as article_comment_path(comment.article, comment)
I suggest you to read about nested resources to understand better
A little earlier in the guide, you can see they setup up some associations between an article and a comment. An article has many comments and a comment belongs to an article.
The reason there is an array for the deletion of the comment is because you have to know the article the comment belongs to and the comment itself because of those associations. Since comments belong to articles, we need the article id to find the comment we want to delete. That's why it's comment.article. We're finding the article the comment belongs to.
If you write article.comment, you're getting a comment of the article, not an article of the comment.
Question
In a polymorphic model, thats used in nested controllers, how can I abstract my delete link's path so I'm not hardcoding upload_permitted_user_path(#permissible, permitted_user)?
Details
I have a polymorphic model called permitted users. Basically theres a bunch of objects in my application where we need to control who can see it. So a post, photo, etc can have permitted users.
I want to be able to delete permitted users on the post#edit, photo#edit, etc pages.
I have this line:
# Used in "posts#edit"
<%= link_to 'Delete',
post_permitted_user_path(#permissible, permitted_user), # This should not be hardcoded.
method: :delete,
data: { confirm: 'Are you sure?' } %>
# Used in "photos#edit"
<%= link_to 'Delete',
photo_permitted_user_path(#permissible, permitted_user), # This should not be hardcoded.
method: :delete,
data: { confirm: 'Are you sure?' } %>
How can I abstract the path so I'm not hardcoding <MY_TOP_LEVEL_CLASS>_permitted_user_path(#permissible, permitted_user)?
Found the answer (feel free to repost and I'll accept :P)
Creating polymorphic links is easy using "polymorphic routes".
You can easily generate the proper link using polymorphic_url([#top_resource, #next_level_resource]) in any view.
For example:
polymorphic_url([:admin, #article, #comment]) becomes admin_article_comment_url(#article, #comment).
Another example without the leading :admin:
polymorphic_url([#article, #comment]) becomes article_comment_url(#article, #comment).
Need some tips on how to achieve.
Normally we would do this for scaffold:
rails g scaffold User email:string password_hash:string password_salt:string (and exit)
However, due to the complexity of what I want to achieve in mind, thus the following:
Admin users, where "Admin" is the namespace
rails g scaffold Admin::User email:string password_hash:string password_salt:string (and etc)
Items, where "Application" is the namespace. Items is a model that will be access by different namespaces.
rails g scaffold Application::Item name:string cost:float quantity:integer
So, obviously "Admin" namespace is used for Administrators to login and they could manage items.
However, I'm creating the rails app in such that I has multiple login for different application such as
Namespaces(again)
- Application1
- Application2
So you would expect the main page of each Application to be below as respectively:
http://my.railsapp.com/application1/index
http://my.railsapp.com/application2/index
However, I want to achieve scaffolding, thus Application::Item can be used in any namespace like Application1::Item.
Example would be in my controller(application_a/items_controller)
#items = Application1::Item.all
And in my views, like rails scaffolding
...
<% #items.each do |item| %>
<tr>
<td><%= item.email %></td>
<td><%= link_to 'Show', item %></td>
<td><%= link_to 'Edit', edit_application1_item_path(item) %></td>
<td><%= link_to 'Destroy', item, method: :delete, data: { confirm: 'Are you sure?' } %></td>
</tr>
<% end %>
...
To explain my reasons. I would like Admin to create, and edit any field for Items.
Application1 to edit the cost or quantity
and finally Application2 to update only the quantity.
Ofcourse, its more to just restrictions, but to put it simply is different application utilising on the same data model.
Thanks.
Scaffolding is for most common cases. If it can't meet your need on customization, then don't use it, create models and controllers separately. No need to waste time on digging that.
When I generated a scaffold for a model class named menuitem, it generates a class file for menuitem, but in the database creates menu_item and refers to the class in the views and controller as menu_item. This may be causing me quite a headache as im genereating a newsted show link, but its failing telling me missing method. The rake routes tells me the show route should be menu_menu_item, but when i do:
<td><%= link_to 'Show', menu_menu_item(#menu) %></td>
it doesnt work.
Is that because of the funky two word class name?
You must have generated either menu_item or menuItem or MenuItem. It doesn't know where one word stops and another starts unless you tell it.
Also, for your link_tos, you just need to append _path:
<td><%= link_to 'Show', menu_menu_item_path(#menu) %></td>
Well, actually, that looks a little wrong to me. That looks like you're trying to go to a single item, which I think will require you to specify both the menu and the item:
<td><%= link_to 'Show', menu_menu_item_path(#menu, #menu_item) %></td>
And to all the menu items in a menu:
<td><%= link_to 'Show', menu_menu_items_path(#menu) %></td>
The Pluralizer shows you show things should be named according to the Rails' conventions.