If I have the following edit method :
def edit
#pet = Pet.find(params[:id])
end
and the following update method :
def update
#pet = Pet.find(params[:id])
if #pet.update_attributes(pet_params)
redirect_to(:action => 'show', :id => #pet.id)
else
render('index')
end
end
could I simply use the edit method in the update method as in :
def update
edit
if #pet.update_attributes(pet_params)
redirect_to(:action => 'show', :id => #pet.id)
else
render('index')
end
end
Controller actions shouldn't call other actions. If there is overlap between the two (such as #pet = Pet.find(params[:id]) this can be done via before_action:
class PetsController < ApplicationController
before_action :set_pet, only: %i[edit update]
def edit
end
def update
if #pet.update_attributes(pet_params)
redirect_to(:action => 'show', :id => #pet.id)
else
render('index')
end
end
private
def set_pet
#pet = Pet.find(params[:id])
end
end
http://guides.rubyonrails.org/action_controller_overview.html#filters
Related
This is my controllers and routes
I have a albums controller and a bands controler with their models, and I want to access the foreign key to pass it, but it told me bands is blank
def show
#album = Album.find_by(:id => params[:id])
render :show
end
def new
#band = Band.find_by(:id => params[:band_id])
#albums = Album.new(:band_id => params[:band_id])
render :new
end
def create
#albums = Album.new(albums_params)
if #albums.save
flash[:success] = "Album created successfully"
redirect_to album_path(#albums.id)
else
#band = #albums.band
flash[:error] = #albums.errors.full_messages
render :new
end
end
def update
render :edit
end
def edit
end
def destroy
end
private
def albums_params
params.require(:albums).permit(:name, :band_id, :live, :year)
end
end```
resources :bands do
resources :albums, :only => :new
end
Try to pass Band relation like below.
def new
#band = Band.find_by(:id => params[:band_id])
#albums = Album.new(:band => #band)
render :new
end
OR check your code. Can you find Band with correct id?
#band = Band.find_by(:id => params[:band_id])
AND check your Views
You must put someting like below
<%=form.hidden_field :band_id, value: #albums.band_id%>
OR
<%=form.hidden_field :band_id, value: #band.id %>
My To-do list application, I want to be able to undo changes.
If the task(item) is completed or deleted, I have an undo hyperlink to revert the change.
I am using the paper trail gem file.
The URL is http://localhost:3000/versions/315/revert
Looks like it is not reverting back and picking the item, to revert the change.
I have the versions controller with fi statements that if the Item is blank, return record not found. As it is not returning the message it must have something?
I don't know what I am doing wrong here, any help would be greatly appreciated.
Here is my main Task (item) controller.rb
class ItemsController < ApplicationController
before_action :find_item, only: [:show, :edit, :update, :destroy]
def index
if user_signed_in?
#items = Item.where(:user_id => current_user.id).order("created_at DESC")
end
end
def show
end
def new
#item = current_user.items.build
end
def create
#item = current_user.items.build(item_params)
if #item.save
redirect_to root_path, :notice => "Congrats! Task was created!"
else
render 'new'
end
end
def edit
end
def update
if #item.update(item_params)
redirect_to item_path(#item), :notice => "Congrats! Task was updated!"
else
render 'edit'
end
end
def destroy
#item.destroy
redirect_to root_path , :notice => "Task was Deleted! To Revert click. #{undo_link}"
end
def complete
#item = Item.find(params[:id])
#item.update_attribute(:completed_at, Time.now)
redirect_to root_path , :notice => "To undo completed task please click. #{undo_link}"
end
private
def item_params
params.require(:item).permit(:title, :description)
end
def find_item
#item = Item.find(params[:id])
end
def undo_link
view_context.link_to("undo",revert_version_path(#item.versions.scope.last), :method => :post)
end
end
Here is the versions controller
class VersionsController < ApplicationController
PaperTrail::Version.where('created_at < ?', 1.day.ago).delete_all
def revert
#item = Item.find(params[:id])
if #item.blank?
format.html {redirect_to(rooth_path, :notice => 'Record not found') }
elsif #item.reify
#item.reify.save!
else
#item.item.destroy
end
link_name = params[:redo] == "true" ? "undo" : "redo"
link = view_context.link_to(link_name, revert_version_path(#item.next, :redo => !params[:redo]), :method => :post)
redirect_to :back, :notice => "Undid #{#item.event}. #{link}"
end
end
Here is my Item model (tasks)
class Item < ActiveRecord::Base
belongs_to :user
has_paper_trail # you can pass various options here
def completed?
!completed_at.blank?
end
end
Here is my routes folder
Rails.application.routes.draw do
post "versions/:id/revert" => "versions#revert", :as => "revert_version"
devise_for :users
resources :items do
member do
patch :complete
end
end
root 'items#index'
get '/about' => 'page#about'
end
I have a resumes controller and a welcomes controller in my app. The welcomes controller has only an index action which is there for the root page. The purpose of the resumes controller is to upload(new/create), download(download) etc. pdf files as an logged in user and it works great so far.
I want to implement the resume download feature on the rootpage as well.(welcomes_controller /index).
How can I accomplish this?
Since I can not call the variable to access the resume model from the welcomes controller. How should the routes be? What should I modify on the welcomes_controller?
routes.rb
Rails.application.routes.draw do
devise_for :users
root 'welcomes#index'
resources :resumes do
get :download, on: :member
end
get '*path' => redirect('/')
end
resumes_controller.rb
class ResumesController < ApplicationController
around_filter :catch_not_found
before_action :find_resume, only: [ :show, :edit, :update, :destroy, :download ]
before_action :authenticate_user!
def show
end
def new
if #resume = current_user.resume
redirect_to #resume
else
#resume = Resume.new
end
end
def create
#resume = current_user.build_resume(resume_params)
if #resume.save
redirect_to #resume
else
render :new
end
end
def edit
end
def update
if #resume.update resume_params
redirect_to #resume, notice: "Your resume was successfully saved!"
else
render 'edit'
end
end
def destroy
#resume.destroy
redirect_to new_resume_path, notice: "Your resume was successfully deleted!"
end
def download
send_data #resume, type: "application/pdf", disposition: "attachment"
end
private
def resume_params
params.require(:resume).permit( :user_id, :download_file, :remove_download_file)
end
def find_resume
#resume = Resume.find(params[:id])
end
def catch_not_found
yield
rescue ActiveRecord::RecordNotFound
redirect_to(root_url, :notice => 'Record not found')
end
end
resumes/show.html.erb
...
...
<%= link_to "Download", download_resume_path(#resume), "data-turbolinks" => false %>
welcomes_controller.rb
class WelcomesController < ApplicationController
def index
end
end
you can access Resume model from welcome controller, although controller is welcome you can call Resume model see sample below
class WelcomesController < ApplicationController
def index
# you can call Resume model frome here
#resumes = Resume.all
end
def show
#resume = Resume.find(params[:id])
end
end
I have A comments Controller Where a User can go tip the comment (right now clicking it will flash a sentence, will add functions later). The problem I'm having is that the page is loading but when I click the link nothing is happening. I'm getting this error when I go to the console:
AbstractController::ActionNotFound - The action 'tip' could not be found for CommentsController
But I have the action in our controller here:
def tip
redirect_to :back, :notice => "Thank you for the Tip. The User will Love it"
end
Here's the route for the tip:
get "/:id/tip" => "comments#tip", :as => "tip"
Here's the Link_to also"
= link_to(tip_path(comment), :class => "story-likes-link", :remote => true, :title => "Tip comment" ) do
%i.fa.fa-money.fa-lg
Tip
Thank you so much for the help : )
edit: whole Controller
class CommentsController < ApplicationController
before_action :authenticate_user!
before_action :set_user
before_action :set_resource, :except => [:destroy]
before_action :set_parent, :except => [:destroy]
before_action :set_comment, :only => [:update, :destroy]
respond_to :js
# Create the comments/replies for the books/comics
def create
#comment = current_user.comments.new(comment_params)
if #comment.save
#comment.move_to_child_of(#parent) unless #parent.nil?
end
respond_with #comment, #resource
end
# Update the comments for the user
def update
#comment.update_attributes(comment_params)
respond_with #comment, #resource
end
# Delete the comments for the books/comics
def destroy
#resource = #comment.commentable
#comment.destroy
respond_with #resource
end
private
# Permitted parameters
def comment_params
params.require(:comment).permit(:body, :commentable_id, :commentable_type, :parent_id)
end
# Set the parent resource for the comments and replies
def set_resource
if params[:comment][:commentable_type].eql?("Book")
#resource = Book.find(params[:comment][:commentable_id])
else
#resource = Comic.find(params[:comment][:commentable_id])
end
end
# Set the parent for the comments to make then as the child of the parent
def set_parent
#parent = params[:comment].has_key?(:parent_id) ? Comment.find(params[:comment][:parent_id]) : nil
end
# Set the comment for the source
def set_comment
#comment = Comment.find(params[:id])
end
def tip
redirect_to :back, :notice => "Thank you for the Tip. The User will Love it"
end
def set_user
#user = User.find(params[:user_id])
end
end
The problem is the action #tip method is hidden in private section, so the router sees not the method at all.
Well, then move the method's code above the private keyword:
def tip
redirect_to :back, :notice => "Thank you for the Tip. The User will Love it"
end
private
NOTE: that action method should not be place to private or protected sections, only to public, which is default section for ruby class definition flow.
New to Rails. New to OOP. I have a client and action_item model. An action item (a todo) has many and belongs to many clients. A client, has many action items. Essentially: A user, creates TODO's, from client pages.
User: creates a client (Crayola LLC, for ex) with crud.
User is then on the Client's show page (Crayola LLC's show page).
My question is, HOW TO have: User to be able to create an action item, for that client. Example: Call Crayola, to sell them an upgrade).
Created join table called action_items_clients, with foreign keys client_id, and action_item_id. Ran migration. Just have no idea how to facilitate creation of action items FOR clients. As it stands, action items can be created without clients. That's simple crud. This is where my novice understanding of rails hits roadblocks.
Action Items Controller:
class ActionItemsController < ApplicationController
def index
#action_items = ActionItem.all
end
def new
#action_items = ActionItem.new
end
def create
#action_item = ActionItem.new(action_items_params)
if #action_item.save
redirect_to(:action => 'show', :id => #action_item.id)
#renders client individual page
else
redirect_to(:action => 'new')
end
end
def edit
#action_item = ActionItem.find(params[:id])
end
def update
#action_item = ActionItem.find(params[:id])
if #action_item.update_attributes(action_items_params)
redirect_to(:controller => 'action_items', :action => 'show', :id => #action_item.id)
flash[:notice] = "Updated"
else
render 'new'
end
end
def show
#action_item = ActionItem.find(params[:id])
end
def action_clients
#action_clients = ActionItem.Client.new
end
def delete
#action_items = ActionItem.find(params[:id])
end
def destroy
#action_items = ActionItem.find(params[:id]).destroy
redirect_to(:controller => 'action_items', :action => 'index')
end
private
def action_items_params
params.require(:action_item).permit(:purpose, :correspondence_method, :know_person, :contact_name_answer, :additional_notes)
end
end
Clients controller
class ClientsController < ApplicationController
def index
#clients = Client.all
end
def new
#client = Client.new
end
def create
#client = Client.new(clients_params)
if #client.save
redirect_to(:action => 'show', :id => #client.id)
#renders client individual page
else
redirect_to(:action => 'new')
end
end
def edit
#client = Client.find(params[:id])
end
def update
#client = Client.find(params[:id])
if #client.update_attributes(clients_params)
redirect_to(:action => 'show', :id => #client.id)
else
render 'edit'
end
end
def show
#client = Client.find(params[:id])
end
def delete
#client = Client.find(params[:id])
end
def destroy
#client = Client.find(params[:id]).destroy
redirect_to(:controller => 'clients', :action => 'index')
end
private
def clients_params
params.require(:client).permit(:name)
end
end
Show page for each client:
<div align="center"><h1> <%= #client.name %> </h1></div>
<ol><li><%= link_to('Enter Definition Mode', :controller => 'action_items', :action => 'new', :id => #client.id) %></br></br></li>
<li><%= link_to('Back to client List', :controller => 'clients', :action => 'index') %> </li></br>
</ol>
The way I would do this is setup your routes so that action_items are nested under the client, something like so:
# /clients/13/action_items
resources :clients do
resources :action_items
end
Or if the user logging in is a client or only has one client, then you could skip that, and just have resources :action_items.
Then if you direct a user to /clients/13/action_items, then they will hit action_items#index, and params[:client_id] will be set to 13. You can use this to scope the action_items throughout that controller.
As long as you have the relationships setup between Client and ActionItem setup:
class Client < ActiveRecord::Base
belongs_to :user
has_and_belongs_to_many :action_items
end
class ActionItem < ActiveRecord::Base
has_and_belongs_to_many :clients
end
It is probably also good to scope that to the currently logged in user:
class User < ActiveRecord::Base
has_many :clients
end
but it depends on how you want things to work. This is probably how I'd structure things:
class ActionItemsController < ApplicationController
before_action :get_client
def index
#action_items = #client.action_items.all
end
def new
#action_items = #client.action_items.new
end
def create
#action_item = #client.action_items.new(action_items_params)
if #action_item.save
redirect_to(:action => 'show', :id => #action_item.id, :client_id => #client.id)
else
redirect_to(:action => 'new')
end
end
# and other actions....
private
def get_client
#client = current_user.clients.find(params[:client_id])
end
end
EDIT (to address some commented questions):
If the action_items aren't always scoped to a client, they can live under both a nested and an un-nested route at the same time:
# /action_items
resources :action_items
resources :clients do
# /clients/13/action_items
resources :action_items
end
Then the before_action can be a bit more generic to set the owner to either the client, or the user itself (as long as User also has_and_belongs_to_many :action_items):
class ActionItemsController < ApplicationController
before_action :get_owner
def index
#action_items = #owner.action_items.all
end
# ... other stuff
private
def get_owner
if params[:client_id].present?
#owner = current_user.clients.find(params[:client_id])
else
#owner = current_user
end
end
end
Your redirects will probably need to take into account whether they came from a nested page or not, so you might have some logic like this around them:
def destroy
item = #owner.action_items.find(params[:id])
item.destroy
if params[:client_id]
redirect_to client_action_items_path(params[:client_id])
else
redirect_to action_items_path
end
end
Your link_tos will also have to change similarly, here's a link to the above destroy action:
<% if params[:client_id].present? %>
<%= link_to 'Delete action item', client_action_item_path(params[:client_id], #action_item), :method => 'delete' %>
<% else %>
<%= link_to 'Delete action item', #action_item, :method => 'delete' %>
<% end %>