Create multiple records in rails with collection_select - ruby-on-rails

I am trying to build a messaging system where the user can select multiple recipients, and I would like to system to create a message for each of them. So far I got the controller like:
def create
#listmail = params[:recipient_ids]
#listmail.each do |v|
#message = current_user.messages.build(:title, :description, :user_id, recipient_id: v, :recipient_email, :tag_id, :industry, :strategy, :position, :aum)
#message.save!
end
if #message.save
redirect_to messages_path
else
render 'new'
end
end
The view:
<%= simple_form_for #message do |f| %>
<%= f.collection_select(:recipient_id, User.all, :id, :full_name, {}, class: "selectpicker", title: "Choose recipent", multiple: true, data: { style: "form-control", size: "20", width: "100%" }) %>
<%= f.input :title, label: "Message Title" %>
<%= f.cktext_area :description, label: "Message Body" , :input_html => {:rows => 15} %>
<%= f.button :submit, :class => 'btn-custom' %>
<% end %>
But I get the error:
/Users/apulvard/Desktop/villas/app/controllers/messages_controller.rb:40: syntax error, unexpected ',', expecting => ...ipient_id: v, :recipient_email, :tag_id, :industry, :strateg... ... ^ /Users/apulvard/Desktop/villas/app/controllers/messages_controller.rb:54: syntax error, unexpected keyword_end, expecting end-of-input
What am I not doing well please?

/Users/apulvard/Desktop/villas/app/controllers/messages_controller.rb:40:
syntax error, unexpected ',', expecting => ...ipient_id: v,
:recipient_email, :tag_id, :industry, :strateg... ...
You are passing an invalid hash to the build method. The hash must be a combination of key-value pairs. For example
Not valid:
h = {:email, :name, user: 1}
SyntaxError: (irb):4: syntax error, unexpected ',', expecting =>
h = {:email, :name, user: 1}
^
Valid:
h = {email:"", name: "", user: 1}
=> {:email=>"", :name=>"", :user=>1}
Solution:
You should change your hash to key-value pairs with the params that are coming to the create method. Usually this was the case before Rails 4. In Rails4, strong params were introduced. If you are using Rails 4+, you should define the strong params like so
def create
#listmail = params[:recipient_ids]
#listmail.each do |v|
#message = current_user.messages.build(message_params)
#message.recipient_id = v
#message.save!
end
if #message.save
redirect_to messages_path
else
render 'new'
end
end
private
def message_params
params.require(:message).permit(:title, :description, :user_id, :recipient_id, :recipient_email, :tag_id, :industry, :strategy, :position, :aum)
end
Moreover,
#listmail = params[:recipient_ids]
should be
#listmail = params[:message][:recipient_id]

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 }

Why is a permitted variable throwing an undefined method error?

Going from ruby-2.1.2#rails-3.2.18 to ruby-2.4.0#rails-5.0.2.
The following stanza throws an undefined method `reqdb' for #
<%= form_for :page, :url => {:odbinsight => :reqdb} do |f| %>
<% if #mywkbks != nil %>
<%= f.collection_select :reqdb, #mywkbks,:name, :title,
:include_blank => false, :prompt => "Workbook?" %>
<% end %>
I have also used #reqdb, same error:
The log shows data from the controller:
DEBUG:0:#reqdb = nil |#reqpg = nil|#name = nil|#mywkbks=[#<Page id: 1,
name: "NB001", title: "Something">, #<Page id: 2, name: "dev", title:
"dev prototyp workbook">]
(Note: pglmt and reqdb are not table column names, but temp variables.):
def page_params
params.require(:page).permit(:name, :title, :body,
:accs_level, :parent_id, :navlabel,
:position, :redirect,
:action_name,:controller_name, :pglmt, {:reqdb => []})
end
I have also had the stanza with just:
....pglmt, reqdb) with the same error resulting.
Thanks for your comments.

Rails rails-money undefined method `cents'

I am attempting to implement money-rails and I have run into the following error:
undefined method `cents' for "1,000.00":String
I followed this tutorial to get a "fancy" currency input field.
DB schema:
t.integer "balance_cents", default: 0, null: false
t.string "balance_currency", default: "USD", null: false
Model:
monetize :balance_cents, as: :balance, allow_nil: :true, :numericality => { :greater_than_or_equal_to => 0}
def initialize(attributes = {})
super
#balance = attributes[:balance]
end
Form:
<%= form_for(#asset, url: assets_new_path) do |f| %>
<%= f.label :balance, "Balance / Value ($)" %>
<%= f.number_field :balance, data: { role: 'money', a_sep: ',', a_dec: '.' }, class: 'form-control' %>
Controller:
def create
#asset = Asset.new(asset_params)
if #asset.save
flash[:success] = "New asset created successfully."
redirect_to assets_path
else
...
end
end
def asset_params
params.require(:asset).permit(:balance)
end
Should I be setting up the :balance input field another (better) way?
According to the Rails Guides the best way to do it in the after_initialize callback.
Use balance_satangs for your column instead of balance_cents

Action Mailer with form that accepts_nested_attributes with Rails/HAML

My model setup:
class Contact < ActiveRecord::Base
belongs_to :restaurant
end
class Restaurant < ActiveRecord::Base
has_many :contacts
accepts_nested_attributes_for :contacts
end
Contact class, trying to target: :email
class Contact < ActiveRecord::Base {
:id => :integer,
:first_name => :string,
:last_name => :string,
:role => :string,
:email => :string,
:phone => :string,
:webmaster_email => :string,
:restaurant_id => :integer,
:created_at => :datetime,
:updated_at => :datetime
}
My create action:
def create
stripe_token = PayingCustomer.build(restaurant_params)
#restaurant = Restaurant.new(restaurant_params)
#restaurant.payment_profile_id = stripe_token.id
#restaurant.generate_token
respond_to do |with|
if #restaurant.payment_profile_id != nil && #restaurant.save
RestaurantMailer.signup_confirmation(#restaurant).deliver
with.html {redirect_to restaurants_path(token: #restaurant.token)}
else
with.html {render :new }
end
end
end
I'm not sure how to target the email entered on form below:
=form_for #restaurant, html: {id: 'signup-form', multipart: true} do |f|
=field_set_tag %q{First, tell us about your restaurant.}, class: 'restaurant' do
=label_tag '', '', for: 'restaurant_name'
=f.text_field :name, placeholder: 'Restaurant Name'
=label_tag '', '', for: 'restaurant_address'
=f.text_field :address, placeholder: 'Street Address'
=label_tag '', '', for: 'restaurant_city'
=f.text_field :city, placeholder: 'City'
=label_tag '', '', for: 'restaurant_state_province'
=f.text_field :state_province, placeholder: 'State/Province'
=label_tag '', '', for: 'restaurant_country'
=select_tag 'restaurant[country]', "<option value='canada'>Canada</option><option value='usa'>United States</option>".html_safe, id: 'country'
=field_set_tag %q{Provide us with your contact info.}, class: 'contact' do
=f.fields_for :contacts do |contact_fields|
=label_tag '', ''
=contact_fields.text_field :first_name, placeholder: 'First Name'
=label_tag '', ''
=contact_fields.text_field :last_name, placeholder: 'Last Name'
=label_tag '', ''
=contact_fields.text_field :role, placeholder: 'Role at Restaurant'
=label_tag '', ''
=contact_fields.text_field :email, placeholder: 'Email'
=label_tag '', ''
=contact_fields.text_field :phone, placeholder: 'Phone'
=label_tag '', ''
=contact_fields.text_field :webmaster_email, placeholder: "Webmaster's Email (optional)"
My target is =contact_fields.text_field :email, placeholder: 'Email'
I'm trying to find out how I would write this in my mailer, with current setup, I'm getting wrong number of arguments (1 for 2)
My problem is right here:
class RestaurantMailer < ActionMailer::Base
default from: "from#example.com"
def signup_confirmation(restaurant, contact)
#greeting = "Hi"
#restaurant = restaurant
#contact = contact
mail to: contact.email, subject: "Sign up Confirmation"
end
end
Edit:
def signup_confirmation(restaurant)
#greeting = "Hi"
#restaurant = restaurant
#contact = restaurant.contact
mail to: #contact.email, subject: "Sign up Confirmation"
end
Results in:
NoMethodError in RestaurantsController#create
undefined method `contact' for #<Restaurant:0x007fd686c593b0>
Any help would be greatly appreciated!
it looks like your problem is in RestaurantMailer.signup_confirmation(#restaurant).deliver because you pass one argument, when signup_confirmation expects two.
in this case you could write:
def signup_confirmation(restaurant)
#greeting = "Hi"
#restaurant = restaurant
#contact = restaurant.contacts.first
mail to: #contact.email, subject: "Sign up Confirmation"
end

Adding a string in front of a parameter for a form

I want to add a string in front of a paramemter on my form so that when the user submits something on the form, it posts to an external API and my client can log in to freshdesk.com, and instead of saying BOB, it will say Hello from BOB.
Hello from [:username]
I tried this in my view:
= f.text_field "Hello From, #{:username}"
but it does not work. I also tried to use a value:
= f.text_field :subject, value: "Hello From, #{:username}"
but that does not work either. Here is my form:
= form_for(:contacts, url: contacts_path) do |f|
= f.error_messages
= f.label :subject, "Name"
%span{style: 'color: red'} *
= f.text_field :subject, class: "text_field width_100_percent"
%br
%br
= f.label "Email"
%span{style: 'color: red'} *
%br
= f.email_field :email, class: "text_field width_100_percent"
%br
%br
= f.label "Question(s), and/or feedback"
%span{style: 'color: red'} *
%br
= f.text_area :description, class: "text_field width_100_percent", style: 'height: 100px;'
%br
%br
= f.submit "Submit", class: 'btn btn-warning'
Here is my controller:
def new
#contacts = Form.new
end
def create
#contacts = Form.new(params[:contacts])
#contacts.post_tickets(params[:contacts])
if #contacts.valid?
flash[:success] = "Message sent! Thank you for conacting us."
redirect_to new_contact_path
else
flash[:alert] = "Please fill in the required fields"
render action: 'new'
end
end
this is from my model
class Form
include ActiveModel::Validations
include ActiveModel::Conversion
include ActiveModel::Translation
extend ActiveModel::Naming
attr_accessor :config, :client, :subject, :email, :custom_field_phone_number_28445,
:custom_field_name_28445, :custom_field_company_28445, :description,
:custom_field
validates_presence_of :subject, :message => '^Please enter your name'
validates_presence_of :description, :message => '^Question(s), and/or feedback can not be blank'
validates :email, presence: true
validates_format_of :email, :with => /^[-a-z0-9_+\.]+\#([-a-z0-9]+\.)+[a-z0-9]{2,4}$/i
def initialize(attributes = {})
attributes.each do |name, value|
#attributes = attributes
end
self.config = YAML.load_file("#{Rails.root}/config/fresh_desk.yml")[Rails.env]
self.client = Freshdesk.new(config[:url], config[:api_key], config[:password])
end
def read_attribute_for_validation(key)
#attributes[key]
end
def post_tickets(params)
client.post_tickets(params)
end
def persisted?
false
end
end
Your view should have straightforward fields with no magic. We'll use the Form class to do the complicated stuff.
= f.text_field :subject
The method call to post_tickets does not need to receive the params because the Form object has already been initialized with the params values. Also, you shouldn't post the ticket, I think, unless the object is valid, right?
def create
#contacts = Form.new(params[:contacts])
if #contacts.valid?
#contacts.post_tickets
flash[:success] = "Message sent! Thank you for contacting us."
redirect_to new_contact_path
else
flash[:alert] = "Please fill in the required fields"
render action: 'new'
end
end
Your Form model should be responsible for modifying the :subject parameter to include the prefix:
class Form
# Some code omitted
def initialize(attributes = {})
attributes.each do |name, value|
send("#{name}=", value)
end
end
def post_tickets
client.post_tickets({
:whatever_fields => whatever_attribute,
:username => username,
:subject => "Hello from, #{subject}",
:email => email,
:description => description
})
end
end
That way, the Form object has the correct values as submitted by the user, but you've overwritten the posted subject so that it will return the combined string you want, with "Hello from..."
In post_tickets, reference directly the parameters you want to send, via the attributes with which the Form object has been initialized. In the case of subject, you'll send a combined value instead.
Please note, I've rewritten your initialize to use a more classic attribute setting method.
Thoughts, questions?
You should do this in your model by adding your substring before the value que form will send you. This seems business logic, it shouldn't be in the view.
def post_tickets(params)
client.username = "Hello From, " + client.username
client.post_tickets(params)
end

Resources