How To Create Action Items For Specific Customer In Rails - ruby-on-rails

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

Related

the foreign key is just passing to the new form not the create action (ruby on rails)

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

Trying to implement friends with Rails, getting a No Route Matches [GET] error when I try to route to any controller method

I've been searching for an answer for the past few hours and I'm really frustrated, I'm new to Rails so I feel like it is a stupid mistake or I just did something totally wrong. Please help me fix this error. Here is my controller:
class FriendshipsController < ApplicationController
before_action :authenticate_user!
before_action :def_user, only: [:create, :accept, :deny, :destroy]
def index
#user = current_user
#friends = #user.friends.paginate :page => params[:page]
#pending_friends = #user.pending_friends.paginate :page => params[:page]
end
def show
end
def create
Friendship.request(#user1, #friend)
flash[:success] = "Friend request has been sent to #{#user2.screenname}."
redirect_to user_path(#friend)
end
def accept
Freindship.accept(#user1, #friend)
flash[:success] = "Friend request from #{#user2.screenname} has been accepted."
redirect_to friends_path
end
def deny
Friendship.breakup(#user1, #friend)
flash[:success] = "Friend request from #{#user2.screenname} has been declined."
redirect_to friends_path
end
def destroy
Friendship.breakup(#user1, #friend)
flash[:success] = "#{#user2.screenname} has been successfully removed from your friends list."
redirect_to friends_path
end
private
def def_user
#user1 = current_user
#friend = User.find(params[:id])
end
end
and my model:
class Friendship < ActiveRecord::Base
belongs_to :user
belongs_to :friend, :class_name => 'User'
def self.request(user, friend)
unless user == friend or Friendship.exists?(user, friend)
transaction do
create(:user => user, :friend => friend, :status => 'pending')
create(:user => friend, :friend => user, :status => 'requested')
end
end
end
def self.accept(user, friend)
transaction do
accepted_at = Time.now
accept_one_side(user, friend, accepted_at)
accept_one_side(friend, user, accepted_at)
end
end
def self.breakup(user, friend)
transaction do
destroy(find_by_user_id_and_friend_id(user, friend))
destroy(find_by_user_id_and_friend_id(friend, user))
end
end
private
def self.accept_one_side(user, friend, accepted_at)
request = find_by_user_id_and_friend_id(user, friend)
request.status = 'accepted'
request.accepted_at = accepted_at
request.save!
end
end
In my routes file I have
get 'friends/create', to: 'friendships#create', as: 'add_friend'
The line in my views that calls it is this:
<%= link_to "Send Friend Request", add_friend_path(current_user, #user), method: :get, class: "btn btn-default" %>
I have also tried
<%= link_to 'Send Friend Request', {:controller => 'friendships', :action => 'create', :id => #user.id}, :class => "btn btn-default" %>
Sorry for my noobiness, and thank you so much for any help.
Please consider to add in :id to the route.
get 'friends/create/:id', to: 'friendships#create', as: 'add_friend'
You trying to get def_user by params[:id], but id is not present in the route.
Just a thougths:
That code
flash[:success] = "Friend request from #{#user2.screenname} has been accepted."
will fail with
undefined method `screenname' for nil:NilClass
because it's no #user2 defined.
Also pleae note that CRUD implementation assumes to use different HTTP methods as POST, PUT and DELETE
So, for creating a friendship POST method suits better than GET. The same for deny and destroy actions.
Rails provides convenient resource routes http://guides.rubyonrails.org/routing.html#resource-routing-the-rails-default
Hope that helps.

Rails - Cannot seem to pass a parameter to a method

I am trying to set up a simple cart. I want to be able to click on 'add' on a record and then have that item added to the cart with the id of the record/line
<% #documents.each do |document| %>
<td><%= link_to "add", add_to_cart_path(8), :method => :post %></td>
<% end %>
I put the add_to_cart_path(8) as a troubleshooting. I really want that to be add_to_cart(document.id) however, either way, the current doc id parameter is not getting passed to the creation of the new item record.
My route is
post '/add_to_cart/:doc_id' => 'carts#add_to_cart', :as => 'add_to_cart'
The carts controller has
def add_to_cart
$current_doc_id = doc_id
current_cart.add_item(:doc_id)
redirect_to carts_path(current_cart.id)
end
my cart model has
def add_item(document_id)
#line_item = Item.create(:document_id => document_id, :cart_id => $cart_number)
if #line_item.save
# flash[:success] = "item added!"
else
# flash[:fail] = "!"
end
end
When I look at the items table, the record is being created and the cart id is properly populated. However, the document_id field is 'null'.
I know you have the answer, but I wanted to clean up your code a bit...
#config/routes.rb
resources :cart, only: [] do
collection do
post "add/:document_id", to: :create #-> url.com/cart/add/:document_id
delete "remove/:document_id", to: :destroy #-> url.com/cart/remove/:document_id
end
end
#app/controllers/cart_controller.rb
class CartController < ApplicationController
before_action :set_document
def create
current_cart.line_items << #document
redirect_to carts_path current_cart.id
end
def destroy
current_cart.line_items.delete #document
redirect_to carts_path current_cart.id
end
private
def set_document
#document = Document.find params[:document_id]
end
end
This would allow you to use:
<%= link_to "Add to Cart", carts_add_path(#document), method: :post %>
<%= link_to "Remove from Cart", carts_remove_path(#document), method: :delete %>
You have a fundamental antipattern in that you're using request-based logic in your Cart model. Models should only be for data-driven logic; request logic all needs to be kept within your controller:
if #line_item.save
# flash[:success] = "item added!"
else
# flash[:fail] = "!"
end
... needs to be in your controller if you're relying on it to form a response.
We've set up a cart before (using a session model). You may benefit from the code we used. It's fundamentally different to yours in that it keeps the cart in a single session cookie, rather than saving the data in a model:
#config/routes.rb
resources :cart do
collection do
post 'cart/add/:id', to: 'cart#add', as: :cart_add
delete 'cart/remove(/:id(/:all))', to: 'cart#delete', as: :cart_delete
end
end
#app/controllers/cart_controller.rb
class CartController < ApplicationController
include ApplicationHelper
#Index
def index
#items = cart_session.cart_contents
end
#Add
def add
session[:cart] ||={}
products = session[:cart][:products]
#If exists, add new, else create new variable
if (products && products != {})
session[:cart][:products] << params[:id]
else
session[:cart][:products] = Array(params[:id])
end
#Handle the request
respond_to do |format|
format.json { render json: cart_session.build_json }
format.html { redirect_to cart_index_path }
end
end
#Delete
def delete
session[:cart] ||={}
products = session[:cart][:products]
id = params[:id]
all = params[:all]
#Is ID present?
unless id.blank?
unless all.blank?
products.delete(params['id'])
else
products.delete_at(products.index(id) || products.length)
end
else
products.delete
end
#Handle the request
respond_to do |format|
format.json { render json: cart_session.build_json }
format.html { redirect_to cart_index_path }
end
end
end
Then to show the cart:
#app/views/cart/index.html.erb
<% #items.each do |item| %>
<%= item.name %>
<% end %>
I'll delete if inappropriate, I figured it would give you some perspective.

Using edit method in update method in Rails

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

Passing Values to Controllers

I'm trying to allow users to 'favorite' links (that is, create a new Favorite record with their user_id and the link_id) This is what I have so far..
When I click favorite (as a user), the new record is assigned to the user_id but the link_id field is nil. How can I pass the link_id into my FavoritesController?
My View Code
Added Link Model Code
class FavoritesController < ApplicationController
def create
#user = User.find(session[:user_id])
#favorite = #user.favorites.create :link_id => params[:id]
redirect_to :back
end
end
The Favorite model belongs to :user and :link
Note: I've also tried this but when I click 'favorite', there's an error "Couldn't find Link without an ID."
Update
<%= link_to "Favorite", :controller => :favorites, :action => :create, :link_id => link.id %>
with
class FavoritesController < ApplicationController
def create
#user = User.find(session[:user_id])
#favorite = #user.favorites.create :link_id => :params[:link_id]
redirect_to :back
end
end
Returns "can't convert Symbol into Integer"
app/controllers/favorites_controller.rb:4:in []
app/controllers/favorites_controller.rb:4:in create
I've tried forcing it into an Integer several ways with .to_i
You could try the following
In your view:
<%= link_to "Favorite", :controller => :favorites, :action => :create, :link_id => link.id %>
In your controller:
class FavoritesController < ApplicationController
def create
#user = User.find(:first, :conditions => ["id = ?", session[:id]])
#favorite = #user.favorites.create
#favorite.update_attributes(:link_id => params[:link_id])
redirect_to :back
end
end
Just as a side note, when finding records, i tend to use:
.find(:first, :conditions => ["id = ?", session[:id]])
as it will escape most stuff submitted by user and returns one record.
I have broken the steps up in your controller, but you could combine them into one like this:
def create
#user = User.find(:first, :conditions => ["id = ?", session[:id]])
#favorite = #user.favorites.create(:link_id => params[:link_id])
redirect_to :back
end
hopefully that should work
You should try:
#user = User.find(session[:user_id])
#favorite = Favorite.create :link_id => params[:id]
#favorite.user = #user
It fails because params[:id] is nil. Put
throw params
at the beginning of the create method and check what you have available there. Please psot also the code in your view.

Resources