I've been borrowing some code from an old ruby on rails 3 app of mine for a new rails 4 app. The code works on the old site, but on the new one it doesn't.
Here's my routes.rb:
scope ':username' do
resources :recipes
end
get "/:username" => "recipes#index"
Here's my controller index:
def index
#user = User.find_by_username params[:username]
#recipes = Recipe.all
end
and my view:
<% #recipes.each do |recipe| %>
<tr>
<td><%= recipe.name %></td>
<td><%= link_to recipe.name, recipe_path(username: #user.username, id: recipe.id) %></td>
<td><%= link_to 'Edit', edit_recipe_path(recipe) %></td>
<td><%= link_to 'Destroy', recipe, method: :delete, data: { confirm: 'Are you sure?' } %></td>
</tr>
<% end %>
but the error pops up:
undefined method `username' for nil:NilClass
but the current user username is set to test so its not nil, and the error shouldn't be popping up.
Thanks for all help!
When you have variables that could be potentially be nil, you should handle the case where they are nil, unless you explicitly wish for it to fail in these circumstances.
The dynamic find_by methods return nil when no records are found. Hence, as alfonso pointed out, you are getting a null value for #user.
In this particular instance, I would question how you are using the username param; if recipes are associated with recipes, then I would set up a has_many :recipes in my user model, and belongs_to :user in my recipe model.
Since the user is the 'parent' here, I would opt to create a recipes action in the UsersController. It seems more logical to me to put the recipes that belong to a user in the user's controller, and access the recipes as a collection from the user.
Alternatively, if you are trying to show recipes associated with a user, I would put the action in the RecipesController, and get the user that belongs to a recipe by using the #user method set up from the belongs_to relationship in the database.
In either case, you'll want to guarantee that the users and/or any recipes are defined before trying to render a page. You might want to display a 501 error or something similar if a user doesn't exist that's trying to be accessed, etc.
If you insist that there should always be a user for a recipe, then you should add that type of validation to the recipes model, so that adding a recipe without a user is disallowed:
validates :user, :presence => true
Sorry if I went a little off tangent.
get "/:username"
Here - :username is in position of id
#user = User.find_by_username params[:username]
Here you are trying to find it by params.
Link should look like this http://localhost:3000/user_name/user_name?username=user_name
to find some user, and it is obviously not what you want to achieve.
get "/:id" => "recipes#index"
#user = User.find(params[:id])
Related
Hello I have a lists of invoices that belong to a business and also the business belongs to a user, I am trying to have a button (link to) on a table in which all the invoices are listed for the user to be able to update the status of the invoice.
Pretty much if the user hits the link it will change from paid: true to paid: false and viseversa.
here are the routes:
resources :businesses do
resources :invoices
end
Here is the section of the table in which the link is:
<% if invoice.paid %>
<td><%= link_to "Mark as Not Paid", business_invoice_path(current_user, invoice), method: 'put', data: {paid: false} %></td>
<% else %>
<td><%= link_to "Mark as Paid", business_invoice_path(current_user, invoice), method: 'put', data: {paid: true}%></td>
<% end %>
Note: The paid column is a boolean on the db
Since, the paid column is present on Invoice, it is much better if you handle it at the controller or model level instead of getting the value from the form.
Remove if else conditions and combine it as below:
<%
invoice_text = invoice.paid ? 'Mark as Not Paid' : 'Mark as Paid'
%>
<td><%= link_to invoice_text, business_invoice_path(invoice), method: :put %></td>
In the Business::InvoicesController you can write the logic in update like this:
Business::InvoicesController < ApplicationController
before_action :authenticate_user!
before_action :set_invoice
def update
# TODO: find the business using the invoice
# have a check in place to authorize the
# transaction (if invoice belongs a business which
# belongs the current_user or not, if not then raise Unauthorized error)
# if business does belongs to the current_user then proceed to next step
# invert the value of paid column based on existing value
#invoice.update(paid: !#invoice.paid)
end
private
def set_invoice
#invoice = Invoice.find(params[:id])
end
end
With logic above, you can forget about maintain/finding the value of paid column since you have an option to revert the value of paid to true and back to false. Also, that I assumed you are using Devise for authentication.
My app uses devise and devise_invitable to handle invitations. On my new invitations view I have a table that lists all users that have been invited. I want to add a column to delete each listed user. If I understand correctly, the route expects a format that should probably be something like remove_user_invitation_path(something) and also that it appears to be an invitation_token (an attribute of the User model). I just don't know how to insert it into the URL as a format. I'm also not sure if this will delete the user itself or the invitation (* I placed the) given that a user can accept or ignore an invitation, I worry that it will only work for users that have a pending invitation. Any input is appreciated!
$ rails routes
remove_user_invitation GET /users/invitation/remove(.:format) users/invitations#destroy
# users/invitations/new
<% #invited_users.each do |invited| %>
<tr>
<td><%= invited.email %></td>
<td><%= link_to "Delete", remove_user_invitation_path, method: :delete, data: { confirm: "Are you sure you want to delete this user?" } %></td>
<tr>
<% end %>
# GET /resource/invitation/remove?invitation_token=abcdef
def destroy
resource.destroy
set_flash_message :notice, :invitation_removed if is_flashing_format?
redirect_to after_sign_out_path_for(resource_name)
end
# What my variable contains
#invited_users = User.where.not(invitation_sent_at: nil)
I'm somewhat new to rails. I'm going through making the classic twitter clone right now. I want to have a search bar on my homepage that allows the user to search for a twitter handle, and if the handle exists, it will send the user to the show page for that twitter handle.
I've been following a RailsCast on how to implement a simple search, but instead of doing it on the index like the video, I want to do it on the show action. I've run into some problems though. The form sits on my user index view.
Here is the error:
ActionController::UrlGenerationError in Users#index
Showing c:/Sites/Projects/twitterapp/twitter/app/views/users/index.html.erb where line #2 raised:
No route matches {:action=>"show", :controller=>"users"} missing required keys: [:id]
Here is the form:
<%= form_tag(user_path, method: 'get') do %>
<%= text_field_tag(:search, params[:search]) %>
<%= submit_tag("Search", name: nil) %>
<% end %>
Here is my show action:
def show
#user = User.search(params[:search])
end
And here is my search method in my user model:
def self.search(search)
if search
find(:all, conditions:['name LIKE ?', "%#{search}%"])
else
find(:all)
end
end
Actually you cannot use the show method as a search result finder. Because according to the rails convention:
For any resource like users, rails scaffold generates index,new, show, create, update, delete methods based on your routes files.
Thus based on the conventional way, show method always asks for an object. Lets say you are using UserContoller show method. It asks for a user object. Which you haven't provide in the form. that's why :id missing error is given.
I would tell you to do some more learning. And for searching create a different method in a different controller and define that controller method to the routes.rb file. This is the best way to do.
If you still want to use the show method, then change the show methods routing from the routes.rb file. You've to manually declare the show action on routes file.
you are using user_path and path need to inform id from present user
you can do this in action :index but I recommend you to create a action to this
view
<%= form_tag(search_users_path, method: 'get') do %>
<%= text_field_tag(:search, params[:search]) %>
<%= submit_tag("Search", name: nil) %>
<% end %>
routes.rb
resources :users do
post 'search', :on => :collection
end
users_controller.rb
def search
#user = User.search(params[:search])
end
You should to create a view search.html.erb similar as index.html.erb
As Emu and Breno pointed what causing the problem user_path requires an user id
Solution idea:
Why not just point to users index action? like this:
<%= form_tag(users_path, method: 'get') do %>
<%= text_field_tag(:search, params[:search]) %>
<%= submit_tag("Search", name: nil) %>
<% end %>
users_controller.rb:
def index
if params[:search]
#user = User.search(params[:search])
end
end
and you can use ajax remote: true to handle the returned user object
Found your question via Google, but the responses and suggestions didn't work for me. Found another solution that did, so seems worth posting here.
"Search and Filter Rails Models Without Bloating Your Controller":
http://www.justinweiss.com/articles/search-and-filter-rails-models-without-bloating-your-controller/
Here's the problem I've been working on for the last few days:
I have task and completed_task models:
class Task < ActiveRecord::Base
belongs_to :user
has_many :completed_tasks
end
class CompletedTask < ActiveRecord::Base
belongs_to :task
end
I have a form that says:
<% #tasks.each do |task| %>
<td><%= link_to task.description, task_path(task) %></td>
<td><%= task.user.first_name %></td>
<td><%= task.value %></td>
<td><%= task.task_type %></td>
<td><%= task.frequency %></td
<td><%= task.active %></td>
<td><%= task.due_by %></td>
<%= button_to "Task Completed", new_completed_task_path(:completed =>[:task_id =>
task.id, :task_value => task.value}) %>
<%end%>
In my completed_task_controller, I have:
def new
#completed_task = CompletedTask.new(params[:completed])
end
def create
#completed_task = CompletedTask.new(completed_task_params)
end
When I click on the button to complete a task, I want it to create a record in the completed_tasks table but the params from the parent table are not flowing from the new action to the create action. I'm guessing it has to do with the strong parameters which I have set as:
private
def set_completed_task
#completed_task = CompletedTask.find(params[:id])
end
def completed_task_params
params.require(:completed_task).permit(:task_id, :task_value)
end
Here is the error I am getting:
ActiveModel::ForbiddenAttributesError
Extracted source (around line #19):
def new
#completed_task = CompletedTask.new(params[:completed])
end
Any ideas???
When you call the new method, at that point nothing has been returned from the form (it hasn't even been dsiplayed, yet)
You should just do
def new
#completed_task = CompletedTask.new
end
When the form is returned, then the create method would typically do
def create
#completed_task = CompletedTask.new(completed_task_params)
if #completed_task.save
# stuff to do when the record is saved, maybe redirect to show page or index
else
# stuff to do if record is not saved... probably redisplay new format with errors
end
end
EDIT: to clarify, the method completed_task_params (which you correctly coded) essentially flags the form attributes as acceptable. Had you done CompletedTask.new(params[:completed_task]) strong parameters would've been unhappy as the attributes weren't flagged as permitted.
I have a strange BUG in a nested resources thing. I have a Seller/Car relationship where a Seller has_many Cars. Now I need to delete a Car from those that belong to a seller (the seller is authentication through Sessions#create and its id is stored in session[:seller_id]:
This is the Car Index View listing all cars that belong to this particular seller:
<%= #cars.each do |car| %>
<tr>
<td><%= car.brand %></tr></td>
<td><%= link_to 'Edit', edit_seller_car_path(session[:seller_id], car) %></td>
<td><%= link_to 'Destroy', seller_car_path(session[:seller_id], car) %></td>
</tr>
<% end %>
Standing on the Car index listing all cars belonging to the authenticated seller, the URL looks like localhost:3000/sellers/2/cars. This page displays the table listing cars with the edit and destroy buttons (edit works fine)
Here is the Cars#destroy:
#seller = Seller.find(params[:seller_id])
#car = #seller.cars.find(params[:id])
#car.destroy
flash[:notice] = "Deleted"
redirect_to seller_cars_url(session[:seller_id])
Here is the Cars#edit (it's working fine when editing):
#seller = Seller.find(params[:seller_id])
#car = #seller.cars.find(params[:id])
When I click on the destroy button, this BUG happens:
1) Strangely, the car is still deleted successfully.
2) I get routing error:
No route matches {:action => "edit", :controller => "cars", :seller_id=>nil, id=>#<Car id:23, brand:"Opel"...
Looks like the problem here arises in the Edit button in the index page. The car it's referring to is the first car in the table and the seller_id is nil here, meaning the seller_id is no longer held in session[:seller_id], which is the act of Sessions#destroy
3) I noticed that the Seller is logged out. As if the destroy in Car controller invokes the Destroy in Sessions controller that clears the session.
4) The URL seems to be the correct one for the Cars index: localhost:3000/sellers/2/cars, but I still see that routing error mentioned in 2.
I would appreciate if you please help guide me in this strange behavior. Thank you.