Let's say I have the simple rails blog app.
And I have a custom action, like page_views which shows the number of views of the post.
class PostsController < ApplicationController
def page_views
#show some page views
end
end
And there is also an associated view in the app/views/Posts folder.
Now, in the routes.rb I have:
map.resources :posts
map.resources :posts, :collection => {
:page_views=> :get
}
in my posts show.html.erb file I have a link to the page_views view:
link_to("View Page Views",page_views_posts_path + "/" + post.id.to_s)
Another paths:
page_views_posts_path(post)
page_views_path(post)
page_views_posts(post)
Have either resulted in method not found or an incorrect url, like:
http://localhost:3000/posts/page_views.#<posts:0xabcdef00>
I would assume the url should be:
http://localhost:3000/posts/page_views/1
So, what I am missing here?
If you want to provide page_views page for each view you should declare extra action not as collection method but as a member method:
map.resources :posts, :member => { :page_views=> :get }
Also if you want this for all posts as well (show the ranking table of some sorts) add the same parameter as collection action:
map.resources :posts, :member => { :page_views=> :get }, :collection => { :page_views => :get }
This way you'll have following routes generated:
page_views_post_path(post) # for single post
page_views_posts_path # for all posts
You can check new routes by running following command:
$ rake routes | grep page_views
You will get only those associated views considering the fact that you haven't declared them for other controllers.
You can also add custom methods to resources in routes.rb like this:
resources :posts do
collection do
get :page_views
end
end
And use page_views_posts_path to access to the custom method.
Related
i'm new to mvc, rails and web development and i'm facing a problem:
I have an action(show) and a view for this action.
The view for show submits a form_tag to another action, that renders the action show.
The problem is, I have no idea how to set a route for the action that renders show.
Right now my routes.rb is:
resources :meals do
collection do
get "meals/:id", to: "meals#show"
end
end
Tried to add these but didn't work:
match "meals/:id/calculate" , :to => "meals#calculate",:via => [:get]
and:
get "meals/:id/calculate", to => "meals#calculate"
resources :meals generate path to show action.
Run rake routes or open in a browser 'http://localhost:3000/rails/info/routes` to see list of generated routes.
To add calculate use member:
resources :meals do
get :calculate, on: :member
end
I'm trying to call a custom controller action shuffle for a resource that is nested within another resource. I can't seem to get the method call right.
routes.rb
resources :templates do
resources :items
end
match "/templates/:template_id/items/shuffle" => "items#shuffle"
I have a link in my items#index view:
<%= link_to 'Shuffle', shuffle_template_items_path(#template) %>
When I click on the link, I get the following error:
undefined method `shuffle_template_items_path' for #<#<Class:0x42577c8>:0x3e77578>
I have also tried <%= link_to 'Shuffle', template_items_shuffle_path(#template) %> and that did not work.
How do I correctly call this custom action?
You probably want this:
resources :templates do
resources :items do
get :shuffle, :on => :collection
end
end
If you want your custom action to have a name, you need to provide it:
match "/templates/:template_id/items/shuffle" => "items#shuffle", :as => :suffle_template_items
I think the best way to write shuffle is in collection as per the documentation of Rails Routes:
So it would looks like this:
resources :templates do
resources :items do
collection do
get :shuffle
end
end
end
when you try rake routes you will find shuffle_template_items GET /templates/:template_id/items/shuffle(.:format) items#shuffle.
Following attempt seems to be functional, but is not the 'clean' result that I am trying to archive.
Following route
get "/learn(/:category)", to: "users#index", as: "learn"
Should be useable for something like "/learn/technology" - Which works, if entered manually in the address bar.
If I tough try to achieve similar in my views, I get the following: "/learn?category=technology" - Which well, technically works, but is not what I want.
I'm using the following inside my view:
- Category.promoted.limit(7).each do |category|
%li.category-button
= link_to learn_path(category) do
= button_tag "", :class => "#{category.name}"
= content_tag(:span, category.to_s, :class => 'category-head')
And my Category Model looks the following:
class Category < ActiveRecord::Base
has_many :skills
validates_uniqueness_of :name
scope :promoted, lambda { where(:promoted => true) }
def to_s
read_attribute(:name).titleize
end
def to_param
name.parameterize
end
end
How would I achieve the 'cleaner' solution?
Edit:
Following works - but there must be a better solution than that?
get "/learn", to: "users#index", as: "learn"
get "/learn/:category", to: "users#index", as: "filter_learn"
Try changing your link to the following:
...
= link_to learn_path(category: category.name) do
...
You may use url_for to solve the problem.
Suppose I have UsersController with index action and this in routes.rb:
resources :users, only: [:index] do
collection do
get ':kind', :to => 'users#index'
end
end
Then when I'm on /users page I can use the url_for this way:
= link_to 'Kind1', url_for(kind: :students)
which will produce path:
/users/students
If I'm on some another page (another controller or another action), then I shoud provide more info. For example when I'm on another controller's page then I should provide both controller and action params if target action is not index (if target action is index then it is sufficient to provide only controller):
= link_to 'Kind1', url_for(controller: :users, action: :index, kind: :students)
it produces the same path:
/users/students
While using users_path(kind: :students) you'll get:
/users?kind=students
I have a questions controller and an associated model and a number of rest routes. Here is how it's set up in routes.rb:
resources :questions
I want to add a custom route that has the format /questions/widget/ID (where ID is the id of the question for which I want to generate a widget). I want this to be processed by the "widget" action in my questions controller. I've tried a number of things such as:
resources :questions do
member do
get 'widget/:id'
end
end
But nothing is working. I'm sure I'm missing something simple. Any ideas? Thanks in advance.
You do not have to specify the id since you are inside resources. It should look like:
resources :questions do
member do
get 'widget'
end
end
You can get more information from the Rails Guide. Look at section 2.9.1.
Edit: I just noticed that you are trying to match get /questions/widget/:id. This will set up a route for get /questions/:id/widget. This is more in line with Rails convention. If you really want it the other way, you need to set up a custom match statement:
match "/questions/widget/:id" => "questions#widget"
However, I would stick with convention.
I know it is old, but looking to fix another routing problem I ended here, it is possible, to do what you are asking for, here is an example
resources :articles do
get 'by_tag/:tag' => :by_tag, on: :collection
get 'by_author/:author' => :by_author, on: :collection
resources :comments, except: :show
end
now you have /artices/by_tag/:tag . The trick was to use on:collection.
Obviously don't forget to add the by_tag action and by_author.
class ArticlesController < ApplicationController
.....
def by_tag
...
end
end
Check this route works with
melardev#local~$ rails routes
Why don't you use this routes:
resources :questions do
resources :widgets
end
it will create path like questions/:question_id/widgets/new for you to create new widget for question with specific id of question.
This is what ended up working for me:
resources :post do
get "author/:author", to: "posts#author", on: :collection, as: "author"
end
Which outputs the following route:
author_posts GET /posts/author/:author(.:format) posts#author
Then in your controller, you need to create the author action:
class PostsController < ApplicationController
def author
#roles = Post.where(author: params[:author])
render :index # to reuse the index view
end
end
Then in your view:
<%= link_to post.author, author_posts_path(post.author), data: { turbo_frame: "_top" } %>
I'm reading these two pages
resources
Adding more RESTful actions
The Rails Guides page shows
map.resources :photos, :new => { :upload => :post }
And its corresponding URL
/photos/upload
This looks wonderful.
My routes.rb shows this
map.resources :users, :new => { :signup => :get, :register => :post }
When I do: [~/my_app]$ rake routes
I see the two new routes added
signup_new_user GET /users/new/signup(.:format)
register_new_user POST /users/new/register(.:format)
Note the inclusion of /new! I don't want that. I just want /users/signup and /users/register (as described in the Rails Routing Guide).
Any help?
When you expose a controller as a resource, following actions are automatically added:
show
index
new
create
edit
update
destroy
These actions can be categorized in to two groups:
:member actions
The URL for the member action has the id of the target resource. E.g:
users/1/edit
users/1
You can think of :member action as an instance method on a class. It always applies on an existing resource.
Default member actions: show, edit, update, destroy
:collection actions
The URL for the :collection action does not contain the id of the target resource. E.g:
users/login
users/register
You can think of :collection action as a static method on a class.
Default collection actions: index, new, create
In your case you need two new actions for registration. These actions belong to :collection type( as you do not have the id of the user while submitting these actions). Your route can be as follows:
map.resources :users, :collection => { :signup => :get, :register => :post }
The URL for the actions are as follows:
users/signup
users/register
If you want to remove a standard action generated by Rails use :except/:only options:
map.resources :foo, :only => :show
map.resources :foo, :except => [:destroy, :show]
Edit 1
I usually treat the confirmation action as a :member action. In this case params[id] will contain the confirmation code.
Route configuration:
map.resources :users, :member => { :confirm => :get}
URL
/users/xab3454a/confirm
confirm_user_path(:id => #user.confirmation_code) # returns the URL above
Controller
class UsersController < ApplicationController
def confirm
# assuming you have an attribute called `confirmation_code` in `users` table
# and you have added a uniq index on the column!!
if User.find_by_confirmation_code(params[id])
# success
else
# error
end
end
end
This can be taken as just another syntax -- something good to know may be.
Syntax 1:
resources :users do
member do
get 'signup'
post 'register'
end
end
Rake Route Output will include
signup_users GET /users/signup(.:format) {:action=>"signup", :controller=>"users"}
register_users POST /users/register(.:format) {:action=>"register", :controller=>"use
rs"}
Syntax 2:
If you have only one collection route
resources :users do
get 'signup', :on => :collection
end
If i'm understanding your question right, you just want to rename the urls of the new and create actions.
This would be done like so:
map.resources :users, :path_names => {:new => 'signup', :create => 'register'}
If you really would like to add new routes with corresponding controller actions, then Damiens answer is the way to go.
The new option allows you to create new routes for creating new objects. That's why they're prefixed with that term.
What you're looking for is the :collection option.
map.resources :users, :collection => { :signup => :get, :register => :post }
Which will create the /users/signup and /users/register urls.