Rails nested Resources not working in controller - ruby-on-rails

I have this nested resource:
resources :services do
resources :users do
put "assign" => "services#users#assign", as: :assign
end
end
My form contains this:
<%= button_to 'submit', service_user_assign_url(service.id, abstractor.id), method: :put %>
this generates the following url, which looks fine to me:
http://localhost:3000/services/1/users/2/assign
and the following is in my services controller:
def assign
#service = Service.find(params[:service_id])
#service.users << User.find(params[:user_id])
redirect_to dashboards_path
end
However I get this error:
The action 'users' could not be found for ServicesController
I'm not sure what this means - I have a has and belongs to many relationship between users and services and I am trying to associate an existing user to a service

You need to tell rails if this action is a member action or a collection action, from the url you mentioned that you want to use, it's a member action:
resources :services do
resources :users do
member do
put :assign
end
end
end

Related

Unable to create model based forms rails 4

I have model based form:
<h2>Add New Credit Card</h2>
<%= form_for #credit_card do |f| %>
some fields
<% end %>
routes:
resources :credit_card
credit_card_index GET /credit_card(.:format) credit_card#index
POST /credit_card(.:format) credit_card#create
new_credit_card GET /credit_card/new(.:format) credit_card#new
edit_credit_card GET /credit_card/:id/edit(.:format) credit_card#edit
credit_card GET /credit_card/:id(.:format) credit_card#show
PATCH /credit_card/:id(.:format) credit_card#update
PUT /credit_card/:id(.:format) credit_card#update
DELETE /credit_card/:id(.:format) credit_card#destroy
controller:
def new
#credit_card = CreditCard.new
end
When I try to render by form it says:
undefined method `credit_cards_path' for #<#<Class:0x00000004c37680>:0x00000004c34570>
Did you mean? credit_card_path
credit_card_index_path
credit_card_url
Its a model based form, for now I have nothing in model. I just want to render and submit will go to create method
You're using the Singular Resources:
resources :credit_card
Where you have to use Plural Resources:
resources :credit_cards
In your routes, use plural for resources definition.
resources :credit_cards
That will generate your routes like
credit_cards GET /credit_cards/:id(.:format) credit_card#show
Use resources :credit_cards instead of resources :credit_card

Routing error for rails - uninitialized constant SubscribersController

I have a Subscriber model that takes in a "phone_number" and a "visit" integer. I have two controllers Subscribers and Visits(super and sub) I have never worked with nested controllers before and I'm having some issues with namespace I believe. Because I getting back the uninitialized constant error. Basically the subscriber controller signs up a subscriber and the visit controller counts the amount of times they've visited by user input of their phone_number. Why am I getting this error? I'll show my code for clarity.
CONTROLLERS
class Subscribers::VisitsController < ApplicationController
def new
#subscriber = Subscriber.new
end
def create
#subscriber = Subscriber.find_by_phone_number(params[:phone_number])
if #subscriber
#subscriber.visit += 1
#subscriber.save
redirect_to subscribers_visits_new_path(:subscriber)
else
render "new"
end
end
end
class SubscribersController < ApplicationController
def index
#subscriber = Subscriber.all
end
def new
#subscriber = Subscriber.new
end
def create
#subscriber = Subscriber.create(subscriber_params)
if #subscriber.save
flash[:success] = "Subscriber Has Been successfully Created"
redirect_to new_subscriber_path(:subscriber)
else
render "new"
end
end
ROUTES
Rails.application.routes.draw do
devise_for :users
resources :subscribers, except: :show
get '/subscribers/visits/new', to: 'subscribers/visits#new'
root "welcomes#index"
VIEWS
<h1>hey</hey>
<%= form_for #subscriber do |form| %>
<div class="form-group">
<p>
<%= form.label :phone_number %>
<%= form.text_field :phone_number %>
</p>
<% end %>
ERROR
Hmm, my guess is you are trying to route url subscriber/visits/new to new action in VisitsController?How about changing this line:
get '/subscribers/visits/new', to: 'subscribers/visits#new'
to:
namespace :subscribers do
get '/visits/new', to: 'visits#new'
end
Also try to move this block above resources :subscribers, except: :show if you still get the error.
Cheers
You probably do not need to inherit one controller from another. Simply define the controllers as you normally would:
app/controllers/subscribers_controller.rb
class SubscribersController < ApplicationController
# methods for Subscribers
end
in app/controllers/visits_controller.rb
class VisitsController < ApplicationController
# methods for Visits
end
Note that these must to be located in separate files, so that Rails can find the correct source file by the name of the object that it's looking for. This is a Rails naming convention.
Regarding your routes, you'll need to change to use one of 4 route formats. Reading the section on Adding More RESTful Actions in the Rails Routing from the Outside In guide might help.
1) To route visits as a nested resource, which is what it appears you're actually trying to do, you would use this:
resources :subscribers, except: :show do
resources :visits
end
This will produce these routes:
GET /subscribers/new
POST /subscribers
GET /subscribers
GET /subscribers/:id/edit
PATCH /subscribers/:id/update
DELETE /subscribers/:id/destroy
GET /subscribers/:id/visits/new
POST /subscribers/:id/visits
GET /subscribers/:id/visits
GET /subscribers/:id/visits/:id
GET /subscribers/:id/visits/:id/edit
PATCH /subscribers/:id/visits/:id/update
DELETE /subscribers/:id/visits/:id/destroy
This is the typical route structure for nested resources and separate controllers.
2) To make visits#new a simple collection (non-member) action in the VisitsController, then you likely want this:
resources :subscribers, except: :show do
collection do
get 'visits/new', to 'visits#new'
post 'visits', to 'visits#create'
end
end
This will produce these routes:
GET /subscribers/new
POST /subscribers
GET /subscribers
GET /subscribers/:id/edit
PATCH /subscribers/:id/update
DELETE /subscribers/:id/destroy
GET /subscribers/visits/new
POST /subscribers/visits
This is typically used to add new top-level routes in an existing resource and controller.
3) To construct visits as member actions, use this:
resources :subscribers, except: :show do
member do
get 'visits/new', to 'visits#new'
post 'visits', to 'visits#create'
end
end
This will produce these routes:
GET /subscribers/new
POST /subscribers
GET /subscribers
GET /subscribers/:id/edit
PATCH /subscribers/:id/update
DELETE /subscribers/:id/destroy
GET /subscribers/:id/visits/new
POST /subscribers/:id/visits
This is normally used to add new member routes in an existing resource and controller.
4) To simply make visits routes appear to be included in subscribers, you could use this:
get '/subscribers/visits/new', to: 'visits#new'
post '/subscribers/visits', to: 'visits#create'
resources :subscribers, except: :show
This will produce these routes:
GET /subscribers/visits/new
POST /subscribers/visits
GET /subscribers/new
POST /subscribers
GET /subscribers
GET /subscribers/:id/edit
PATCH /subscribers/:id/update
DELETE /subscribers/:id/destroy
This may be used to make arbitrary routes appear to be included in an existing resource, when they really may be independent.

Nested resource, create action with two IDs

I have this link
link_to "Ban", group_banned_user_path(:group_id => object.group_id, :user_id => object.user.id), :method => :post
That triggers this controller action
def create
#banned_user = BannedUser.new
user = User.find(params[:user_id])
group = Group.find(params[:group_id])
group.ban(user)
redirect_to group
end
Through this route
resources :groups do
member do
post :join
delete :leave
end
resources :banned_users, :only => [:create, :destroy]
end
But I receive a "No route matches [POST] "/groups/4/banned_users/4", because there is no /:id option in the create action url. So my question is how can I access both the user, and group ID from the controller action, since passing them in as seperate arguments in the link_to doesn't work
Your problem is when you have the route /groups/:id/banned_users/:banned_user_id, it will basically require the following params to be present: params[:id] params[:banned_user_id]
--
You're sending this route:
group_banned_user_path(:group_id => object.group_id, :user_id => object.user.id)
This does not populate the :id field. You'll be best doing this:
group_banned_user_path(object.group_id, object.user.id)
--
Update
I think the answer to your question will be to manually declare your routes to accept the :id param:
#config/routes.rb
resources :groups do
post :join #-> you don't need member
delete :leave
resources :banned_users, only: [] do
post ":id", action: "create" #-> groups/:id/banned_users/:id
delete ":id", action: "destroy"
end
end
This will give you the ability to use the route I recommended above

Routes: resources, use member or collection for a custom action?

Hi everyone I am doing a application with rails 3.2. I am trying to use form_tag but I have problem with the route.
I try this in my form:
= form_tag('/companies/save_category', method: "post") do
and this:
= form_tag({:controller => "companies", :action=>"save_category"}, method: "post") do
In my config/routes.rb:
I am a little confused to put route like this
resources :companies do
post 'save_category'
end
or route like this:
resources :companies do
member do
post 'save_category'
end
end
But either way does not work. And when I execute rake routes, I obtain the same result
company_save_category POST /companies/:company_id/save_category(.:format) companies#save_category
The error was this
No route matches [POST] "/companies/save_category"
any idea?
Consider these routes:
resources :companies do
member do
post 'save_category'
end
end
This member block means that the route save_category in the /compagnies/ namespace will need a Company id to work:
/compagnies/12/save_category # where 12 is params[:company_id]
Now, with collection:
resources :companies do
collection do
post 'save_category'
end
end
This means that to get to the save_category route, you don't need the company id:
/compagnies/save_category # will work, is not needing a params[:company_id]
In your case, you should first use the url helpers (generated following the routes.rb). You need here:
if save_category is a *member route*
save_category_company_path(#company)
elsif save_category is a *collection route*
save_category_companies_path
I guess the category you want to save is related to a specific company, right? If yes, you need a member route:
form_tag(save_category_company_path(#company), method: "post") do
Hope this helps!

rails custom rest route with parameter

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" } %>

Resources