Rails 4 - controller not being called - ruby-on-rails

I can hit the importers#import_vendor_ledger action but I can't seem to hit the importers#import_chart_of_accounts action via the redirect_based_on(arg) method inuploaders#upload_file. Please help!
NOTE: I left out some code that I didn't think was necessary to see
My code:
routes.rb
resources :uploaders do
collection { post :upload_file }
end
resources :importers do
collection { post :import_vendor_ledger, :import_chart_of_accounts }
end
index.html.haml
#chart_of_accounts
= form_tag upload_file_uploaders_path, multipart: true do
= hidden_field_tag :account, "chart_of_accounts"
= file_field_tag :file
= submit_tag 'Upload Chart of Accounts'
#vendor_ledger
= form_tag upload_file_uploaders_path, multipart: true do
= hidden_field_tag :account, "vendor_ledger"
= file_field_tag :file
= submit_tag 'Upload'
uploaders_controller.rb
class UploadersController < ApplicationController
include Excel
def upload_file
uploaded_io = params[:file]
if uploaded_io.path.downcase.end_with?(xlsx_extension)
save_to_storage(uploaded_io)
flash[:success] = 'File uploaded successfully!'
redirect_based_on_(params) # this is where it should call the action
else
flash[:warning] = 'ERROR: The file you upload MUST be an ".xlsx" excel file!'
redirect_to root_url
end
end
private
def redirect_based_on_(_params)
case _params[:account]
when "vender_ledger"
redirect_to import_vendor_ledger_importers_path and return
when "chart_of_accounts"
redirect_to import_chart_of_accounts_importers_path and return
end
end
end
importers_controller.rb
class ImportersController < ApplicationController
include Excel
def index
end
def show
end
def import_vendor_ledger # I can hit this action
puts "hits vendor ledger import"
end
def import_chart_of_accounts # but I can't hit this action
puts "hits chart of accounts import"
end
EDIT #1: even if I explicitly call redirect_to import_chart_of_accounts_importers_path in uploaders#upload_file it still doesn't hit the importers#import_chart_of_accounts action
EDIT #2: after inspecting more, it seems that importers#import_chart_of_accounts action IS being hit, but none of the functions in the action is being called

Change your route to something like this:
resources :importers do
collection do
get :import_vendor_ledger
get :import_chart_of_accounts
end
end
EDIT: Since you are redirecting to those two paths, I believe, you need those to be GET routes. Since redirect_to issues a 301 request which will be GET request.

Related

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

How do I pass the select_tag's input to the rails controller

I'm trying to insert the input from a select_tag to my controller method.
I've looked and cannot seem to resolve the issue.
This is the code I have below, nothing for the rank's selection comes up in the params at all.
<h1>hello please set <%= #user.username %>'s rank'</h1>
<%= select_tag 'rank', options_for_select(#ranks.collect{ |r| [r.rank_name] }) %>
<%= button_to "Update", :action => "set_user_rank_update", value: "#{#user.id}", method: :post %>
Update below with the controller and routes
Controller:
class Admin::RankController < ApplicationController
before_action :admin?
def new
#rank = Rank.new
end
def create
#rank = Rank.new(rank_params)
if params["rank"]["admin"].to_i == 1
#rank.toggle! :admin?
end
if #rank.save
flash[:success] = "Rank created"
redirect_to root_path
else
flash[:danger] = "Failed to create rank"
render 'new'
end
end
def set_user_rank_new
#user = User.find_by_id(params["format"])
#ranks = Rank.all
end
def set_user_rank_update
#user = User.find_by_id(params["value"])
#rank = Rank.find_by_id(params["rank"])
#rank_backup = #user.rank.first
debugger
#user.rank - #user.rank.first
#user.rank << #rank
if #user.rank.first == #rank
flash[:success] = "Set user's rank"
redirect_to root_path
else
flash[:danger] = "Failed to set user's rank"
#user.rank - #user.rank.first
#user.rank << #rank_backup
render 'set_user_rank_new'
end
end
private
def rank_params
params.require(:rank).permit(:rank_name, :rank_color)
end
end
Routes
Rails.application.routes.draw do
devise_for :users,
:controllers => { :registrations => "member/registrations" , :sessions => "member/sessions"}
scope module: 'public' do
root 'welcome#index'
end
scope module: 'member' do
get 'members/:id' => 'member#show'
end
scope module: 'admin' do
get 'rank/new' => 'rank#new'
post 'rank/create' => 'rank#create'
get 'rank/set_user_rank/new' => 'rank#set_user_rank_new'
post 'rank/set_user_rank/update' => 'rank#set_user_rank_update'
end
end
Try passing 2 element arrays to options_for_select. The way you have it looks like it would get you option text but no values, which could explain why it doesn't show up in the params.
So for example:
<%= select_tag 'rank', options_for_select(#ranks.collect{ |r|[r.rank_name, r.id] }) %>
The button_to helper creates an inline form with just the parameters included in the helper statement (in this case the user id).
You can check this by examining the resulting HTML on your page - which I think will show an input field for rank sitting outside the form tag.
To include the rank parameter, you should set up the form using a form helper and make sure the rank input is included inside the form.
For what purpose do you need it in your controller action?
With the link_to you can forwards params as well.
It would be very helpful to see your controller and also your routes.

Param is missing or the value is empty: within new

I'm running into this issue working with a form in rails, and was wondering if anybody could take a quick look with it.
My view looks like
= form_for #form_submission do |f|
- if #form_submission.errors.any?
#error_explanation
%h2= "#{pluralize(#form_submission.errors.count, "error")} prohibited this form_submission from being saved:"
%ul
- #form_submission.errors.full_messages.each do |msg|
%li= msg
..and my controller is like so
class FormSubmissionsController < ApplicationController
invisible_captcha only: [:create], on_spam: :handle_spam
def new
#form_submission = FormSubmission.new(form_submission_params)
if #form_submission.save
redirect_to thank_you_path
else
redirect_to root_path
end
end
private
def handle_spam
redirect_to root_path
end
def form_submission_params
params.require(:form_submission).permit(:first_name, :last_name, :organization, :email, :phone)
end
end
Ultimately my problem is that I don't know what params i'm missing. Or even if my value is empty, how would I know, and what could I do to resolve that?
Standard rails approach to forms is your new action is a 'GET' and is used to show the new view for the creation of a resource. The form 'POST's to the create action with the form fields added to the params hash. Your controller methods should be
def new
#form_submission = FormSubmission.new
end
def create
#form_submission = FormSubmission.new(form_submission_params)
if #form_submission.save
redirect_to thank_you_path
else
redirect_to root_path
end
end
Check your routes by running rake routes in the terminal and make sure you have a routes to that point to form_submissions#new and form_submissions#create.
When you click on the submit button you will be able to view the parameters that are being passed in the logs and it should look something like
Parameters: {"utf8"=>"✓", "authenticity_token"=>"someRandomStuff", "form_submission"=>{"first_name"=>"value entered in first_name field", "last_name"=>"value entered in last_name field"}, "commit"=>"Value of submit button"}
Are you really creating a resource called FormSubmission?
I think you're doing it the wrong way.
this is what you should do :
def new
#form_submission = FormSubmission.new
end
def create
#form_submission = FormSubmission.new(form_submission_params)
if #form_submission.save
redirect_to thank_you_path
else
redirect_to root_path
end
end
As said in comments,
"new" action is used to initialize your ressource as empty or with default value.
"create" action is used to save your new ressource

Carrierwave remove method based on params

I have multiple Carrierwave attachment fields in my model which I would like to be able remove in my show view. As there are multiple fields I pass a parameter to my controller method so it knows which file to remove:
def remove_attachment
post = Post.find(params[:id])
post["remove_#{params[:attachment]}!"]
post.save
redirect_to post
end
However, the following line does not work post["remove_#{params[:attachment]}!"]? Not sure where I am going wrong here?
I've tested the following which does work:
def remove_attachment
post = Post.find(params[:id])
post.remove_compliance_guide! # one of the attachment fields
post.save
redirect_to post
end
I realise that I could do the below but I think my first solution is cleaner.
def remove_attachment
post = Bid.find(params[:id])
if params[:attachment] == 'compliance_guide'
post.remove_compliance_guide!
elsif params[:attachment] == 'compliance_other'
post.remove_compliance_other!
elsif params[:attachment] == 'compliance_agreement'
post.remove_compliance_agreement!
end
post.save
redirect_to post
end
Just in case I've made a mistake somewhere else:
route: post 'posts/:id/remove_attachment', to: 'posts#remove_attachment'
view link: link_to 'Remove', { controller: 'post', action: "remove_attachment", attachment: 'compliance_guide', id: #post.id }, method: 'post', class: "file-remove right"
You can use send to invoke a method by passing its name as a string:
def remove_attachment
post = Post.find(params[:id])
post.send("remove_#{params[:attachment]}!")
post.save
redirect_to post
end
Note that a NoMethodError will be raised if the attachment doesn't exist. You could work around that using something like:
if post.respond_to?("remove_#{params[:attachment]}!")
post.send("remove_#{params[:attachment]}!")
post.save
end

Ruby on rails and coupon model

I have really been scratching my head on this and would greatly appreciate help. I have a store setup where people can take courses. I have a course model, order model, and coupon model. Here are the associations in the models
class Course < ActiveRecord::Base
belongs_to :category
has_many :orders
has_many :coupons
end
class Order < ActiveRecord::Base
belongs_to :course
belongs_to :user
belongs_to :coupon
end
class Coupon < ActiveRecord::Base
belongs_to :course
has_many :orders
end
I have a very simple coupon model setup that has code and newprice columns. I want the ability for someone to be able to fill out the coupon form on the new order page and it to update the price.
In my my view for new order I have two forms one for the new order and one for the coupon. How do check in my controller if a user has entered the correct coupon code? How do I update the coupon price to be shown instead of the course price?
here is my order controller
class OrdersController < ApplicationController
before_action :set_order, only: [:show, :edit, :update, :destroy]
before_action :authenticate_user!
def index
#orders = Order.all
end
def show
end
def new
course = Course.find(params[:course_id])
#course = Course.find(params[:course_id])
#order = course.orders.build
#coupon = Coupon.new
#user = current_user.id
#useremail = current_user.email
end
def discount
course = Course.find(params[:course_id])
#order = course.orders.build
#user = current_user.id
#useremail = current_user.email
end
def edit
end
def create
#order = current_user.orders.build(order_params)
if current_user.stripe_customer_id.present?
if #order.pay_with_current_card
redirect_to #order.course, notice: 'You have successfully purchased the course'
else
render action: 'new'
end
else
if #order.save_with_payment
redirect_to #order.course, notice: 'You have successfully purchased the course'
else
render action: 'new'
end
end
end
def update
if #order.update(order_params)
redirect_to #order, notice: 'Order was successfully updated.'
else
render action: 'edit'
end
end
def destroy
#order.destroy
redirect_to orders_url
end
private
# Use callbacks to share common setup or constraints between actions.
def set_order
#order = Order.find(params[:id])
end
# Never trust parameters from the scary internet, only allow the white list through.
def order_params
params.require(:order).permit(:course_id, :user_id, :stripe_card_token, :email)
end
end
You can accomplish this with an AJAX request using the form_for helper with the :remote option.
Summary
Set :remote option to true for your coupons form to submit the AJAX request.
Create controller action to handle the AJAX request from the form.
Use JavaScript to respond to the controller action to update your orders form (the other form in your view) with the new price information, etc.
AJAX request using `:remote`
Here's some example code representing your coupon form :
<%= form_for #coupon, method: :post, url: check_coupon_code_path, remote: true do |f| %>
<%= f.text_field :coupon_code, :placeholder => "Enter your coupon" %>
<%= f.submit "Submit Coupon Code" %>
<% end %>
Notice the following:
The :remote option for the form_for tag is set to true.
The :url option is the path to your controller action in your CouponsController. Because the :remote option is set to true, the request will be posted to this :url option as an AJAX request.
In this code example, it's assuming it has a route defined like this in the routes.rb file to handle the AJAX request for checking the coupon code:
post 'check_coupon_code' => 'coupons#check_coupon_code'
Note: In the forms_for helper, the :url option appends _path to the prefix defined in the routes.rb file.
Bonus note: Use the command rake routes to see the available routes and their respective controller action targets.
Handle AJAX request in the Controller
In your CouponsController, define the action check_coupon_code to handle your AJAX request from the above form_for:
def check_coupon_code
# logic to check for coupon code here
respond_to do |format|
if # coupon code is valid
format.js {}
else
# some error here
end
end
end
Notice the format.js in the respond_to block of the action. This allows the controller to respond to the AJAX request with JavaScript to update your orders form in your view. You'll have to define a corresponding app/views/coupons/check_coupon_code.js.erb view file that generates the actual JavaScript code that will be sent and executed on the client side (or name the JavaScript file check_coupon_code.js.coffee if you're using CoffeeScript).
Updating with JavaScript
The JavaScript in your check_coupon_code.js.erb file will then update the price in your order form.
WARNING: Even if you use JavaScript to change the order price on the client-side (i.e. the browser), it is critical to validate the actual price again in the back-end (i.e. in your controller) in case some malicious user tries to manipulate the browser's request, etc.
You can see the official RailsGuide for another example.

Resources