Rails - Cannot seem to pass a parameter to a method - ruby-on-rails

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.

Related

simple_form_for how to pass params to controller

I want to pass params from event/id(show page) to my order_controller.
I use simple_form_for to pass event.id and promocode that input by user
#event.show.html.haml
= simple_form_for order_url, url: orders_path(#event, :promocode), method: :post do |f|
= f.hidden_field :event_id, params: {id: #event.id}
= f.input :promocode, value: :promocode, class: 'form-control', placeholder: "Enter your PromoCode"
= f.submit 'APPLY PromoCode'
IDK if a need hidden_field to pass event_id
#order_controller
class OrdersController < ApplicationController
before_action :order, only: %i[show]
def index
#orders = Order.all.order(created_at: :desc).page(params[:page]).per(5)
end
def show; end
def create
#order = Order.create(title: event.title, user_id: current_user.id, event_id: event.id, order_amount: event.price, order_currency: event.currency)
if !promo.nil?
redirect_to_order
elsif #order.save
redirect_to checkout_create_path(id: #order.id)
else
redirect_to event, alert: 'Something went wrong, try again later'
end
end
def redirect_to_order
promo_validate
order_amount_promo_code = #order.order_amount - promo.promo_code_amount
order.update(order_amount: order_amount_promo_code)
redirect_to #order
end
def promo_validate
if promo.present? && promo.promo_code_amount.positive? && promo.promo_code_currency == event.currency
promo.update(order_id: #order.id)
else
redirect_to event, alert: "This PromoCode is invalid or Your PromoCode Currency doesn't match with Event"
end
end
private
def promo
#promo ||= PromoCode.find_by(uuid: params[:promocode])
end
def event
#event ||= Event.find(params[:id])
end
def order
#order ||= Order.find(params[:id])
end
def order_params
params.require(:order).permit(:title, :event_id, :promocode, :event)
end
end
I'm using methods def event and def promo to take this params from view.
Also my routes look like this.
resources :events
resources :orders
I would nest the route:
resources :events do
resources :orders, shallow: true
end
This creates an explicit relationship between the two resources that can be seen by just looking at the URL. To create a order tied to an even you send a POST request to /events/:event_id/orders.
class EventsController
def show
# ..
#order = #event.orders.new
end
end
= simple_form_for [#event, #order] do |f|
= f.input :promocode, value: :promocode, class: 'form-control', placeholder: "Enter your PromoCode"
= f.submit 'APPLY PromoCode'
class OrdersController < ApplicationController
# POST /events/:id/orders
def create
#event = Event.find(params[:event_id])
#order = #event.orders.new(title: #event.title, user: current_user order_amount: #event.price, order_currency: #event.currency)
begin
#promo = PromoCode.find_by!(uuid: params[:order][:promocode])
rescue ActiveRecord::RecordNotFound
#order.errors.add(:promocode, 'is invalid')
end
if #order.save
redirect_to checkout_create_path(id: #order.id)
else
redirect_to #event, alert: 'Something went wrong, try again later'
end
end
# ...
end
Other then that your handling of promo codes is very iffy. Instead of monkying around and deducting the rebate from the "amount" by updating the record you should store both the original sales price and the rebate and then calculate the total at checkout - which should also be stored separately. Not doing so amounts to pretty dismal record keeping and might get you in trouble - when it comes to money always play it safe.

Param is missing or the value is empty: vote

I have this newbie error when i want to upvote a "hack" :
ActionController::ParameterMissing at /hacks/6/upvote
param is missing or the value is empty: vote
With Request parameters exemple :
{"_method"=>"post", "authenticity_token"=>"r+fYieTQDsD6fuonr3oe0YEzkzBXH1S8k6bDENS0wCVr3LEpxGA4mps5saM4RQLvBNDVzsm2zXpGm9TKe3ZIYA==",
"controller"=>"hacks", "action"=>"upvote", "id"=>"6"}
I don't understand why my #vote do not appear in parameters...
Controller hacks_controller.rb
class HacksController < ApplicationController
skip_before_action :authenticate_user!, only: [:upvote]
def upvote
#vote = Vote.new(vote_params)
#hack = Hack.find(params[:id])
# raise
#vote.hack = #hack
if #vote.save
redirect_to root_path
else
p 'Problème de #vote.save !'
end
end
private
def vote_params
params.require(:vote).permit(:hack_id, :user_id)
end
end
Model Vote.rb
class Vote < ApplicationRecord
belongs_to :user
belongs_to :hack
validates :hack, presence: true
end
Thanks !
The Rails strong parameters are meant as mass assignment protection and are not suited to this case.
To create an additional CRUD method properly you can just add the additional route to resources:
resources :hacks do
post :upvote
delete :downvote
end
Note that we are using POST not GET as this is a non-idempotent operation.
You also don't need to pass any parameters. :hacks_id will be present in the path and you should fetch the current user id from the session and not the request parameters.
Passing a user id via the parameters is a really bad practice as its very trivial to spoof by using just the web inspector.
class HacksController < ApplicationController
before_action :set_hack!, except: [:new, :index, :create]
# POST /hacks/:hack_id/upvote
def upvote
#vote = #hack.votes.new(user: current_user)
if #vote.save
redirect_to #hack, success: 'Vote created'
else
redirect_to #hack, error: 'Vote could not be created'
end
end
# DELETE /hacks/:hack_id/downvote
def downvote
#vote = #hack.votes.where(user: current_user).first!
#vote.destroy
redirect_to #vote, success: 'Vote deleted'
end
private
# this will raise ActiveRecord::RecordNotFound if
# the id or hack_id param is not valid. This triggers a 404 response
def set_hack!
if params[:id].present?
Hack.find(params[:id])
else
Hack.find(params[:hack_id])
end
end
end
Then in your view you can create the links / buttons like so:
<% if current_user && #hack.votes.where(user: current_user) %>
<%= button_to 'Downvote', hack_downvote_path(#hack), method: :delete %>
<% else %>
<%= button_to 'Upvote', hack_upvote_path(#hack), method: :post %>
<% end %>

ActiveRecord::RecordNotFound in PermitsController#show, Couldn't find Permit with 'id'=

Hi guys I keep get this error when i try to access to SHOW action(Manage permit button on user/show.html.erb) which should display all the permits of a specific user, i search through all my code and couldn't find the bug. Can you guys help me to check which part i did wrong? Btw I'm implementing a website using Ruby on rails
This is my permits_controller.rb
class PermitsController < ApplicationController
before_action :set_permit, only: [:show, :destroy]
def index
#permits = Permit.where(:user_id => current_user.id)
end
def new
#permits = Permit.new
end
def create
#permits = current_user.permits.build(permit_params)
if #permits.save
redirect_to invoice_path
else
render 'new'
end
end
def destroy
Permit.destroy_all(user_id: current_user)
respond_to do |format|
format.html { redirect_to root_path, notice: 'Permit was successfully canceled.' }
format.json { head :no_content }
end
end
def confirm
#fields = %i[vehicle_type, carplate, studentid, name, department, permitstart, permitend]
#permit = current_user.permits.build(permit_params)
render :new and return unless #permit.valid?
end
def show
#permits = Permit.where(:user_id => current_user.id)
end
def update
#permits = Permit.where(user_id: current_user).take
respond_to do |format|
if #permits.update(permit_params)
format.html { redirect_to root_path}
flash[:success] = "Permit successfully updated"
format.json { render :show, status: :ok, location: #user }
else
format.html { render :edit }
format.json { render json: #user.errors, status: :unprocessable_entity }
end
end
end
def edit
#permits = Permit.find(params[:id])
##permits = Permit.find_or_initialize_by(user_id: params[:id])
end
private
# Use callbacks to share common setup or constraints between actions.
def set_permit
#permits = Permit.find(params[:id])
end
# Never trust parameters from the scary internet, only allow the white list through.
def permit_params
params.require(:permit).permit(:vehicle_type, :name, :studentid, :department, :carplate, :duration, :permitstart, :permitend)
end
end
This is my permits/show.html.erb
<%= #permits.name %>
This is my route.db
Rails.application.routes.draw do
resources :users
resources :permits do
collection do
post :confirm
end
end
resources :visitor_permits
root 'static_pages#home'
get 'invoice' => 'permits#invoice'
get 'payment' =>'transaction#new'
get 'show_visitor_permit' =>'visitor_permits#show'
get 'show_permit' =>'permits#show'
get 'visitorpermit' => 'visitor_permits#new'
post 'createpermit' => 'permits#create'
get 'homepage/index'
post 'permits' => 'permits#create'
get 'permitapplication' => 'permits#new'
get 'adminlogin' => 'admin_controller#index'
get 'patrollogin' => 'patrol_officer_controller#index'
get 'createcitation' => 'citations#new'
get 'contact'=> 'static_pages#contact'
get 'about' => 'static_pages#about'
get 'signup' => 'users#new'
get 'help' => 'static_pages#help'
post 'users' => 'users#create'
get 'login' => 'sessions#new' #Page for a new session
post 'login' => 'sessions#create' #Create a new session
delete 'logout'=>'sessions#destroy' #Delete a session
# For details on the DSL available within this file, see http://guides.rubyonrails.org/routing.html
end
users/show.html.erb
<!DOCTYPE html>
<html>
<body>
<div id="sidebar">
<ul id="mySidenav" class="sidenav">
<li><%= link_to "New Parking Permit", permitapplication_path %></li>
<li><%= link_to "Manage Permit", show_permit_path(#permit) %></li>
<li><%= link_to "New Visitor Parking Permit", visitorpermit_path %></li>
<li><%= link_to "Manage Visitor Permit", "#" %></li>
<li><%= link_to "Check Fines", "#" %></li>
<li><%= link_to "New Health and Safety Report", "#" %></li>
<li><%= link_to "Manage Health and Safety Report", "#" %></li>
</ul>
</div>
</body>
</html>
First, you can remove the :show method in before_action as it will invoke two queries.
Then if you add where condition in active record query (like below), it will results the array of records instead of single record.
#permits = Permit.where(:user_id => current_user.id)
So you can not directly put the below line in your view as array of objects will not permit access the name parameter directly.
<%= #permits.name %>
May be you can iterate all the records like below and populate the name of every Permit object.
<% #permits.each do |permit| %>
<%= permit.name %>
<% end %>
Or you can do some thing like below to show it as comma separated.
<%= #permits.map { |f| f.name }.join ',' %>
If you want the first object alone in the #permits you can use like below.
<%= #permits[0].name %>
P.S: Note that it will throw an exception if the first object doesn't exist.
In PermitsController you have have wrote the before action to set the permits
before_action :set_permit, only: [:show, :destroy]
def set_permit
#permits = Permit.find(params[:id])
end
So, while setting permits if permit is not present with specific id in DB it will raise an exception ActiveRecord::RecordNotFound
you are also setting permits in show action
def show
#permits = Permit.where(:user_id => current_user.id)
end
not sure why you are setting permits twice for show action.
- first time in set_permit method
- and second time in show method
Can you please add parameters in question which are passing with the request.
Assigning record twice is not good practice. In your case in before action you are finding Permit by id and if record for that specific id is not present in the database then it will raise a ActiveRecord::RecordNotFound exception. so you have to check what you are passing in params[:id].
If you want to set permits based on the current user you can skip set_permit for show action.
And in show action if you are assigning activerecord collection instead of active record in that case in view you have to iterate over the collection object to print the permit details. Or if there is only one permit for user you can set active record object in show action instead of active record collection

How To Create Action Items For Specific Customer In 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 %>

Create rails record from two ids

The functionality I'm trying to build allows Users to Visit a Restaurant.
I have Users, Locations, and Restaurants models.
Locations have many Restaurants.
I've created a Visits model with user_id and restaurant_id attributes, and a visits_controller with create and destroy methods.
Thing is, I can't create an actual Visit record. Any thoughts on how I can accomplish this? Or am I going about it the wrong way.
Routing Error
No route matches {:controller=>"restaurants", :location_id=>nil}
Code:
Routes:
location_restaurant_visits POST /locations/:location_id/restaurants/:restaurant_id/visits(.:format) visits#create
location_restaurant_visit DELETE /locations/:location_id/restaurants/:restaurant_id/visits/:id(.:format) visits#destroy
Model:
class Visit < ActiveRecord::Base
attr_accessible :restaurant_id, :user_id
belongs_to :user
belongs_to :restaurant
end
View:
<% #restaurants.each do |restaurant| %>
<%= link_to 'Visit', location_restaurant_visits_path(current_user.id, restaurant.id), method: :create %>
<% #visit = Visit.find_by_user_id_and_restaurant_id(current_user.id, restaurant.id) %>
<%= #visit != nil ? "true" : "false" %>
<% end %>
Controller:
class VisitsController < ApplicationController
before_filter :find_restaurant
before_filter :find_user
def create
#visit = Visit.create(params[:user_id => #user.id, :restaurant_id => #restaurant.id])
respond_to do |format|
if #visit.save
format.html { redirect_to location_restaurants_path(#location), notice: 'Visit created.' }
format.json { render json: #visit, status: :created, location: #visit }
else
format.html { render action: "new" }
format.json { render json: #visit.errors, status: :unprocessable_entity }
end
end
end
def destroy
#visit = Visit.find(params[:user_id => #user.id, :restaurant_id => #restaurant.id])
#restaurant.destroy
respond_to do |format|
format.html { redirect_to location_restaurants_path(#restaurant.location_id), notice: 'Unvisited.' }
format.json { head :no_content }
end
end
private
def find_restaurant
#restaurant = Restaurant.find(params[:restaurant_id])
end
def find_user
#user = current_user
end
end
I see a lot of problems here. The first is this line of code in your VisitController's create action (and identical line in your destroy action):
#visit = Visit.create(params[:user_id => #user.id, :restaurant_id => #restaurant.id])
params is a hash, so you should be passing it a key (if anything), not a bunch of key => value bindings. What you probably meant was:
#visit = Visit.create(:user_id => #user.id, :restaurant_id => #restaurant.id)
Note that you initialize #user and #restaurant in before filter methods, so you don't need to access params here.
This line of code is still a bit strange, though, because you are creating a record and then a few lines later you are saving it (if #visit.save). This is redundant: Visit.create initiates and saves the record, so saving it afterwards is pretty much meaningless. What you probably want to do is first initiate a new Visit with Visit.new, then save that:
def create
#visit = Visit.new(:user_id => #user.id, :restaurant_id => #restaurant.id)
respond_to do |format|
if #visit.save
...
The next thing I notice is that you have not initiated a #location in your create action, but you then reference it here:
format.html { redirect_to location_restaurants_path(#location), notice: 'Visit created.' }
Since you will need the location for every restaurant route (since restaurant is a nested resource), you might as well create a method and before_filter for it, like you have with find_restaurant:
before_filter :find_location
...
def find_location
#location = Location.find(params[:location_id])
end
The next problem is that in your view your location_restaurant_path is passed the id of current_user and of restaurant. There are two problems here. First of all the first argument should be a location, not a user (matching the order in location_restaurant_path). The next problem is that for the _path methods, you have to pass the actual object, not the object's id. Finally, you have method: :create, but the method here is referring to the HTTP method, so what you want is method: :post:
link_to 'Visit', location_restaurant_visits_path(#location, restaurant.id), method: :post
You'll have to add a find_location before filter to your RestaurantController to make #location available in the view here.
There may be other problems, but these are some things to start with.
location_id is nil and the path definition doesn't say (/:location_id) forcing a non-nil value there in order to route to that path; create a new route without location_id if you can derive it from a child's attribute (i.e. a restaurant_id refers to a Restaurant which already knows its own location_id).

Resources