Rails Controller Triggering Both Branches Of If-Else Statement - ruby-on-rails

I'm new here so I hope it's appropriate for new accounts to ask questions straight away. I've been working with Rails for sometime now and usually I'm pretty good at researching and solving my own problems - but this one has had me stumped for a few days now.
I have a create action being called in a controller that contains an if-else statement that is conditional based on a check_box post parameter. I can see that the parameter is being posted to the controller so the statement should be able to correctly branch but something strange is happening. The controller executes both branches of the statement, but because my parameters are trimmed depending on that check_box, the second branch always errors out. I'm fairly confident this has little to do with routes.
Please see my code below:
Controller:
def create
#quote = Quote.find(params[:quote_id])
if params[:quote_item][:custom] == 1
#quote_item = #quote.quote_items.new(quote_item_params)
#quote_item.rate = nil
#quote_item.custom = true
#quote_item.unit_price = params[:quote_item][:unit_price]
#quote_item.rate_name = params[:quote_item][:title]
else
#quote_item = #quote.quote_items.new(quote_item_params)
#quote_item.custom = false
#rate = Rate.find(params[:quote_item][:rate_id])
#quote_item.unit_price = #rate.price
#quote_item.rate_name = #rate.product.name
end
respond_to do |format|
if #quote.save
format.html { redirect_to #quote, notice: 'Quote item was successfully added.' }
format.json { render :show, status: :created, location: #quote }
else
format.html { redirect_to #quote }
format.json { render json: #quote.errors, status: :unprocessable_entity }
end
end
View:
<% if #rates.any? %>
<%= bootstrap_form_for([#quote, #quote_item], layout: :inline) do |f| %>
<%= f.text_field :title, class: 'input-sm', hide_label: true, :placeholder => "Line Title" %>
<%= f.select(:rate_id, #rates.collect {|r| [r.select_summary_text, r.id ] }, { hide_label: true }, { :class => 'input-sm' }) %>
<%= f.number_field :quantity, class: 'input-sm', hide_label: true, :min => 1, :length => 2, :value => 1 %>
<%= f.text_field :unit_price, class: 'input-sm', hide_label: true, :min => 1, :length => 2, :prepend => "$" %>
<%= f.text_field :note, class: 'input-sm', hide_label: true, :placeholder => "Note (Optional)" %>
<%= f.submit "Add Quote Item", class: 'btn btn-sm btn-default' %>
<%= f.check_box :custom, label: "Override" %>
<% end %>
<% else %>
<div class="well well-sm"><i class="fa fa-usd"></i> No pricing for this customer has been set. Set product pricing for this jobs customer <%= link_to user_path(#quote.job.user) do %>here.<% end %></div>
<% end %>
My create method errors out here:
#rate = Rate.find(params[:quote_item][:rate_id])
With:
ActiveRecord::RecordNotFound (Couldn't find Rate with 'id'=):
app/controllers/quote_items_controller.rb:19:in `create'
This error is correct though, because the rate ID isn't processed in first branch of the if-else statement. I've tried different check_box form fields, directly overriding 'custom' and both branches still run.
Any help here is greatly appreciated!

The params hash contains strings.
Try swapping:
if params[:quote_item][:custom] == '1'
for your original:
if params[:quote_item][:custom] == 1

Related

rails: Is it possible that my receiver_id failling to be picked?

I am implementing a messaging solution for my rails app, I keep get the following error.
PG::InvalidTextRepresentation - ERROR: invalid input syntax for integer: ""
LINE 1: ...rsations" WHERE ((sender_id = 3 AND receiver_id = '') OR (se...
class MessagesController < ApplicationController
def create
if current_user.id == message_params[:receiver_id]
redirect_to request.referrer, alert: "You cannot send a message to yourself"
end
conversation = Conversation.where("(sender_id = ? AND receiver_id = ?) OR (sender_id = ? AND receiver_id = ?)",
current_user.id, message_params[:receiver_id],
message_params[:receiver_id], current_user.id
).first
if !conversation.present?
conversation = Conversation.create(sender_id: current_user.id, receiver_id: message_params[:receiver_id])
end
#message = Message.new(user_id: current_user.id,
conversation_id: conversation.id,
content: message_params[:content]
)
if #message.save
format.html { redirect_to request.referrer, notice: 'Message was successfully sent.' }
else
format.json { render json: request.referrer.errors, status: :unprocessable_entity }
end
end
Conversation model.
class Conversation < ApplicationRecord
belongs_to :sender, class_name: "User"
belongs_to :receiver, class_name: "User"
def last_message
message = Message.where(conversation_id: self.id).last
if message.present?
return message
else
return Message.new updated_at: Time.now
end
end
end
That happens because the value you're passing for reciever_id is an empty string, but the data type of that column in your database is an integer.
You can use or to make the query, but it'll transform your empty string to nil:
reciever_id = message_params[:reciever_id]
Conversation.where(sender_id: current_user.id, reciever_id: reciever_id)
.or(Conversation.where(sender_id: reciever_id, reciever_id: current_user.id))
Anyway, you might want to check what you're receiving in order to do the query you're performing. It can be assigning default values, returning early, adding constraints to your routes, etc.
Pasting the form directly works but putting it on the partial does not, i.e below this works.
- if current_user.id != profile.user.id
%small
%button.btn.btn-outline-light{"data-target" => "#modal_large", "data-toggle" => "modal", :type => "button"}
Send Message
= simple_form_for Message.new do |f|
= f.error_notification
= f.error_notification message: f.object.errors[:base].to_sentence if f.object.errors[:base].present?
.form-inputs
.row
.col
= f.hidden_field :receiver_id, value: profile.user.id
= f.input :body, autofocus: true, label: "Type Your Message Below"
.form-actions
.row
.col
= f.button :submit, "Send Message", class: "btn btn-primary btn-lg mt-2 float-right"
= render partial: "message", locals: { user: profile.user }
This does not work..
#modal_large.modal.fade{"aria-hidden" => "true", "aria-labelledby" => "modal_large", :role => "dialog", :tabindex => "-1"}
.modal-dialog.modal-lg{:role => "document"}
.modal-content
.modal-header
%h5.modal-title Send a New Message
%button.close{"aria-label" => "Close", "data-dismiss" => "modal", :type => "button"}
%i.ti-close.font-size-14
.modal-body.py-4
= simple_form_for Message.new do |f|
= f.error_notification
= f.error_notification message: f.object.errors[:base].to_sentence if f.object.errors[:base].present?
.form-inputs
.row
.col
= f.hidden_field :receiver_id, value: user.id #profile.user.id
= f.input :body, autofocus: true, label: "Type Your Message Below"
.form-actions
.row
.col
= f.button :submit, "Send Message", class: "btn btn-primary btn-lg mt-2 float-right"
The specific problem is on this line below.
= render partial: "message", locals: { user: profile.user }

Rails has_many update with empty array

There is the following form code:
= form_for #task, html: { class: 'form-horizontal' } do |f|
.form-group
.col-sm-9.col-sm-offset-3
= render partial: 'shared/form_errors', locals: { subject: #task }
.form-group
label.col-sm-3.control-label for='title' Title
.col-sm-9
= f.text_field :title, class: 'form-control', placeholder: 'Title'
.form-group
label.col-sm-3.control-label for='description' Description
.col-sm-9
= f.text_area :description, class: 'form-control', placeholder: 'Description'
.form-group
label.col-sm-3.control-label Teams
.col-sm-9
ul
- Team.all.each do |t|
li
= check_box_tag "team_ids", t.id, #task.teams.include?(t), name: 'task[team_ids][]'
= t.name
.form-group
.col-sm-9.col-sm-offset-3
= f.submit 'Save', class: 'btn btn-success'
As you can see I can select team for my task through checkboxes. My controller:
def update
#task = Task.find(params[:id])
if #task.update(task_params)
redirect_to tasks_path, flash: { alert: TASK_UPDATING_MESSAGE }
else
render 'edit'
end
end
private
def task_params
params.require(:task).permit(:title, :description, team_ids: [])
end
It works good if I update task with some checked teams; but also I want to have ability to check no teams and update taks with empty array of teams. But in this case tasks_params doesn't have team_ids array, and updating doesn't work. How can I fix it? Thanks!
My understanding is that when you submit your form with nothing checked, you have params[:task][:team_ids] = nil.
You can try something like:
def task_params
params[:task][:team_ids] = [] if params[:task][:team_ids].nil?
params.require(:task).permit(:title, :description, team_ids: [])
end
You can do it using collection_check_boxes, just replace the list of teams with:
ul
= f.collection_check_boxes :team_ids ,Team.all, :id, :name do |b|
= content_tag :li, raw("#{b.label { b.check_box } }" + b.object.name)
And this will do the trick.
Note: With this Rails add a hidden field, which fix your issue, also you will fix it only with this:
<input type="hidden" name="task[team_ids][]" value="" autocomplete="off">

Why is this re-direct not working?

Controller
def subscribe
#mailchimp_list_id = "01d4743206"
#gb = Gibbon::API.new
#email = params[:email]
#gb.lists.subscribe({
:id => #mailchimp_list_id,
:email => #email,
:double_optin => false,
:send_welcome => false
})
redirect_to root_path, notice: "Whats up"
end
View
<%= form_tag('/posts/subscribe', method: "post", id: "subscribe", remote: "true") do%>
<div class="large-18 columns text-right mailer">
<%= email_field(:email, :email, {id: "mailersub", placeholder: "Enter your email"}) %>
</div>
<div class="large-6 columns text-right mailer">
<%= submit_tag("Submit", id: "mailersubmit") %>
</div>
<% end %>
All I really want to happen is for the mailchimp process to be completed and then then it re-directs to a path with a notice. The controller and view is for a mailer list form, upon submit I would like this to occur.
Any assistance would be greatly appreciated, thanks!
remote: true do ajax request, it can not redirect. u have to remove remote: true OR at js .on ajax:success do location.url = '/'
Here is your answer, you have to render js inside the controller and inline javascript can be used to redirection:
def subscribe
#mailchimp_list_id = "01d4743206"
#gb = Gibbon::API.new
#email = params[:email]
#gb.lists.subscribe({
:id => #mailchimp_list_id,
:email => #email,
:double_optin => false,
:send_welcome => false
})
flash[:notice] = "Whats up"
respond_to do |format|
format.js { render :js => "/" }
end
end

Ajax file upload rails + Rack RawUpload + Paperclip

I am trying to get ajax image uploading working in my rails app. I am using Paperclip for the normal image uploading, and that works fine, but I can't seem to get the ajax method hooked up. I am using the Rack RawUpload and the
File Uploader plugin. I followed the general instructions here, but I am stuck at the actually attaching the image to the new object on create. Here is my Controller code:
#bottle = Bottle.new(params[:bottle])
#bottle.user_id = current_user.id
#file = params[:qqfile].is_a?(ActionDispatch::Http::UploadedFile) ? params[:qqfile] : params[:file]
is_qq = params.has_key?(:qqfile)
if is_qq
params[:bottle][:image] = params.delete(:file)
render :json => { "success" => true }
else
respond_to do |format|
if #bottle.save
format.html { redirect_to '/bottles', notice: 'Bottle was successfully created.' }
format.json { render json: #bottle, status: :created, location: #bottle }
else
format.html { render action: "new" }
format.json { render json: #bottle.errors, status: :unprocessable_entity }
end
end
end
and Here is the view code:
<%= simple_form_for(#bottle, :html => { :multipart => true }) do |f| %>
<%= f.error_notification %>
<div class="form-inputs">
<%= f.input :brand, :placeholder => 'Brand', :label => false %>
<%= f.input :region, :placeholder => 'Region', :label => false %>
<%= f.input :age, :collection => 5..35, :prompt => "Bottle Age", :label => false %>
<%= f.input :price, :placeholder => 'Price', :label => false, :as => :currency, :input_html => { :class => 'span2' } %>
<%= f.input :image_id, :as => :hidden %>
<%= f.text_area :notes, :placeholder => 'Tasting notes...', :size => "160x5" %>
</div>
</div>
<div class="span3 offset2">
Drag a file from your desktop here...
<div class="well" height="105" style="width:200px;height:300px;">
<!-- <img src="http://placehold.it/200x300" alt="" class="temp_image"> -->
<div id="file-uploader"></div>
</div>
or...
<%= f.file_field :image %>
</div>
<div class="span8 offset2">
<%= f.button :submit, :class => 'btn-primary' %>
<%= link_to bottles_path, :class => 'btn btn-danger' do %>
Cancel
<% end %>
</div>
<% end %>
I am uploading with the File Uploader like this:
var uploader = new qq.FileUploader({
debug: false,
/* Do not use the jQuery selector here */
element: document.getElementById("file-uploader"),
action: '/bottles',
allowedExtensions: ["jpg", "png"],
/*
* This uploads via browser memory. 1 MB example.
*/
sizeLimit: 1048576,
/* Set Article category on submit */
onSubmit: function(id, fileName) {
uploader.setParams({
authenticity_token: $("input[name='authenticity_token']").attr("value")
});
},
onComplete: function(id, fileName, responseJSON){
url = responseJSON.image.image.url;
$('.well').html('<img src="'+url+'" />');
$('input#bottle_image_id').val(responseJSON.image.id);
}
});
It seems to upload using Rack fine and it passes the :file param to the method, but I can't assign param[:bottle][:image] with the param[:file], i get error:
undefined method `[]=' for nil:NilClass
Any help would be greatly appreciated.
EDIT:
So I am able to get the ajax upload to hook into the paperclip upload and add the appropriate parameters, but now I need to update that same object when I submit the rest of the form, not create a new object. How can I store the object created by the ajax upload that contains all the image content and update it once the full form is submitted?
EDIT 2:
The error i get when saving is
undefined method `save' for #<ActiveSupport::HashWithIndifferentAccess:0x007ff961d08e48>
Which I assume is because I am taking the form data and trying to put it in the same #bottle object and the hashes are not matching up.
I patch lib/rack/request.rb at 217 line to solve this problem
replace
{}
with
#env["rack.request.form_hash"] || {}

Easy way to work with date data in the controller?

Working on loading the datetime values being submitted from a datetime_select helper, so i can compare it to the existing saved data.
I'm using the following code, but it feels a bit cumbersome.
DateTime.parse("#{params['start_date(1i)'].to_i}-#{params['start_date(2i)'].to_i}-#{params['start_date(3i)'].to_i} #{params['start_date(4i)'].to_i}:#{params['start_date(5i)'].to_i} -500")
Is there an easier way to build a datetime object?
Edit:
Here's my form code:
= form_for(#charity) do |f|
- if #charity.errors.any?
#error_explanation
%h2
= pluralize(#charity.errors.count, "error")
prohibited this charity from being saved:
%ul
- #charity.errors.full_messages.each do |msg|
%li= msg
.field
= f.label :name, 'Name *'
= f.text_field :name
%fieldset
%legend Dates in Eastern Standard Time
.field
= f.label :start_date, 'Start Date *'
= f.datetime_select :start_date, :default => DateTime.now
.field
= f.label :end_date, 'End Date *'
= f.datetime_select :end_date, :default => DateTime.now
.field
= f.label :percentage, 'Percentage of sales to be donated *'
= f.text_field :percentage
.field
= f.label :charity_number, 'The identification number for your charity *'
= f.text_field :charity_number
.field
Donation options:
%span{ title: "Dollar amounts that you wish to allow people to donate on top of their order.", class: "tooltip_hover" }= image_tag "question.png"
- DonationOption.all.each do |option|
= f.label option.id, number_to_currency(option.amount, unit: "$"), class: "donation_label"
= check_box_tag "charity[donation_option_ids][]", option.id, #charity.donation_option_ids.include?(option.id), class: "donation_check"
.actions
= f.submit nil, class: "btn-primary"
Controller:
def update
#charity = Charity.find(params[:id])
respond_to do |format|
if #charity.update_values(params[:charity])
format.html { redirect_to #charity, notice: 'Charity was successfully updated.', :charity => #charity }
format.json { head :ok }
else
format.html { render action: "edit" }
format.json { render json: #charity.errors, status: :unprocessable_entity }
end
end
end
The update_values method:
def update_values(attributes)
s_date = DateTime.parse("#{attributes['start_date(1i)'].to_i}-#{attributes['start_date(2i)'].to_i}-#{attributes['start_date(3i)'].to_i} #{attributes['start_date(4i)'].to_i}:#{attributes['start_date(5i)'].to_i} -500")
attributes.delete('start_date(1i)')
attributes.delete('start_date(2i)')
attributes.delete('start_date(3i)')
attributes.delete('start_date(4i)')
attributes.delete('start_date(5i)')
# if the charity has not started yet
if DateTime.now.utc < self.start_date.utc
self.start_date = s_date
attributes.merge!(reset_create_job)
end
e_date = DateTime.parse("#{attributes['end_date(1i)'].to_i}-#{attributes['end_date(2i)'].to_i}-#{attributes['end_date(3i)'].to_i} #{attributes['end_date(4i)'].to_i}:#{attributes['end_date(5i)'].to_i} -500")
attributes.delete('end_date(1i)')
attributes.delete('end_date(2i)')
attributes.delete('end_date(3i)')
attributes.delete('end_date(4i)')
attributes.delete('end_date(5i)')
if DateTime.now.utc < self.end_date.utc
self.end_date = e_date
attributes.merge!(reset_delete_job)
end
self.update_attributes(attributes)
end

Resources